diff options
| author | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2015-10-25 12:38:20 +0200 |
|---|---|---|
| committer | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2015-10-25 12:38:20 +0200 |
| commit | 10d15322ed26652263a579bcb59dfd49ab8a30de (patch) | |
| tree | b1497de1b8ffd34b991cdf6e21de1c7a0a65f1de /drivers | |
| parent | 0316d30ea3e66379cd30ed70a114bc282159bb4c (diff) | |
| parent | e5a9f8d04660da7ef3a98260aa74c3976f9cb4cd (diff) | |
Merge remote-tracking branch 'mac80211-next/master' into next
Diffstat (limited to 'drivers')
982 files changed, 40226 insertions, 12191 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 46506e7687cd..a212cefae524 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -315,14 +315,10 @@ static void acpi_bus_osc_support(void) capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; capbuf[OSC_SUPPORT_DWORD] = OSC_SB_PR3_SUPPORT; /* _PR3 is in use */ -#if defined(CONFIG_ACPI_PROCESSOR_AGGREGATOR) ||\ - defined(CONFIG_ACPI_PROCESSOR_AGGREGATOR_MODULE) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PAD_SUPPORT; -#endif - -#if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT; -#endif + if (IS_ENABLED(CONFIG_ACPI_PROCESSOR_AGGREGATOR)) + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PAD_SUPPORT; + if (IS_ENABLED(CONFIG_ACPI_PROCESSOR)) + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT; capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 2614a839c60d..42c66b64c12c 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1044,8 +1044,10 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data) goto err_exit; mutex_lock(&ec->mutex); + result = -ENODATA; list_for_each_entry(handler, &ec->list, node) { if (value == handler->query_bit) { + result = 0; q->handler = acpi_ec_get_query_handler(handler); ec_dbg_evt("Query(0x%02x) scheduled", q->handler->query_bit); diff --git a/drivers/acpi/int340x_thermal.c b/drivers/acpi/int340x_thermal.c index 9dcf83682e36..33505c651f62 100644 --- a/drivers/acpi/int340x_thermal.c +++ b/drivers/acpi/int340x_thermal.c @@ -33,13 +33,12 @@ static const struct acpi_device_id int340x_thermal_device_ids[] = { static int int340x_thermal_handler_attach(struct acpi_device *adev, const struct acpi_device_id *id) { -#if defined(CONFIG_INT340X_THERMAL) || defined(CONFIG_INT340X_THERMAL_MODULE) - acpi_create_platform_device(adev); -#elif defined(INTEL_SOC_DTS_THERMAL) || defined(INTEL_SOC_DTS_THERMAL_MODULE) + if (IS_ENABLED(CONFIG_INT340X_THERMAL)) + acpi_create_platform_device(adev); /* Intel SoC DTS thermal driver needs INT3401 to set IRQ descriptor */ - if (id->driver_data == INT3401_DEVICE) + else if (IS_ENABLED(CONFIG_INTEL_SOC_DTS_THERMAL) && + id->driver_data == INT3401_DEVICE) acpi_create_platform_device(adev); -#endif return 1; } diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 6da0f9beab19..c9336751e5e3 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -372,6 +372,7 @@ static int acpi_isa_register_gsi(struct pci_dev *dev) /* Interrupt Line values above 0xF are forbidden */ if (dev->irq > 0 && (dev->irq <= 0xF) && + acpi_isa_irq_available(dev->irq) && (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) { dev_warn(&dev->dev, "PCI INT %c: no GSI - using ISA IRQ %d\n", pin_name(dev->pin), dev->irq); diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 3b4ea98e3ea0..7c8408b946ca 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -498,8 +498,7 @@ int __init acpi_irq_penalty_init(void) PIRQ_PENALTY_PCI_POSSIBLE; } } - /* Add a penalty for the SCI */ - acpi_irq_penalty[acpi_gbl_FADT.sci_interrupt] += PIRQ_PENALTY_PCI_USING; + return 0; } @@ -553,6 +552,13 @@ static int acpi_pci_link_allocate(struct acpi_pci_link *link) irq = link->irq.possible[i]; } } + if (acpi_irq_penalty[irq] >= PIRQ_PENALTY_ISA_ALWAYS) { + printk(KERN_ERR PREFIX "No IRQ available for %s [%s]. " + "Try pci=noacpi or acpi=off\n", + acpi_device_name(link->device), + acpi_device_bid(link->device)); + return -ENODEV; + } /* Attempt to enable the link device at this IRQ. */ if (acpi_pci_link_set(link, irq)) { @@ -821,6 +827,12 @@ void acpi_penalize_isa_irq(int irq, int active) } } +bool acpi_isa_irq_available(int irq) +{ + return irq >= 0 && (irq >= ARRAY_SIZE(acpi_irq_penalty) || + acpi_irq_penalty[irq] < PIRQ_PENALTY_ISA_ALWAYS); +} + /* * Penalize IRQ used by ACPI SCI. If ACPI SCI pin attributes conflict with * PCI IRQ attributes, mark ACPI SCI as ISA_ALWAYS so it won't be use for diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index fc28b9f5aa84..30d8518b25fb 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -525,8 +525,7 @@ static void acpi_thermal_check(void *data) /* sys I/F for generic thermal sysfs support */ -static int thermal_get_temp(struct thermal_zone_device *thermal, - unsigned long *temp) +static int thermal_get_temp(struct thermal_zone_device *thermal, int *temp) { struct acpi_thermal *tz = thermal->devdata; int result; @@ -633,7 +632,7 @@ static int thermal_get_trip_type(struct thermal_zone_device *thermal, } static int thermal_get_trip_temp(struct thermal_zone_device *thermal, - int trip, unsigned long *temp) + int trip, int *temp) { struct acpi_thermal *tz = thermal->devdata; int i; @@ -686,7 +685,8 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, } static int thermal_get_crit_temp(struct thermal_zone_device *thermal, - unsigned long *temperature) { + int *temperature) +{ struct acpi_thermal *tz = thermal->devdata; if (tz->trips.critical.flags.valid) { @@ -709,8 +709,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal, return -EINVAL; if (type == THERMAL_TRIP_ACTIVE) { - unsigned long trip_temp; - unsigned long temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( + int trip_temp; + int temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( tz->temperature, tz->kelvin_offset); if (thermal_get_trip_temp(thermal, trip, &trip_temp)) return -EINVAL; diff --git a/drivers/atm/he.c b/drivers/atm/he.c index a8da3a50e374..0f5cb37636bc 100644 --- a/drivers/atm/he.c +++ b/drivers/atm/he.c @@ -1578,9 +1578,7 @@ he_stop(struct he_dev *he_dev) kfree(he_dev->rbpl_virt); kfree(he_dev->rbpl_table); - - if (he_dev->rbpl_pool) - dma_pool_destroy(he_dev->rbpl_pool); + dma_pool_destroy(he_dev->rbpl_pool); if (he_dev->rbrq_base) dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), @@ -1594,8 +1592,7 @@ he_stop(struct he_dev *he_dev) dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), he_dev->tpdrq_base, he_dev->tpdrq_phys); - if (he_dev->tpd_pool) - dma_pool_destroy(he_dev->tpd_pool); + dma_pool_destroy(he_dev->tpd_pool); if (he_dev->pci_dev) { pci_read_config_word(he_dev->pci_dev, PCI_COMMAND, &command); diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index 65e65903faa0..7d00f2994738 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -112,7 +112,8 @@ static void ia_enque_head_rtn_q (IARTN_Q *que, IARTN_Q * data) static int ia_enque_rtn_q (IARTN_Q *que, struct desc_tbl_t data) { IARTN_Q *entry = kmalloc(sizeof(*entry), GFP_ATOMIC); - if (!entry) return -1; + if (!entry) + return -ENOMEM; entry->data = data; entry->next = NULL; if (que->next == NULL) @@ -1175,7 +1176,7 @@ static int rx_pkt(struct atm_dev *dev) if (!(skb = atm_alloc_charge(vcc, len, GFP_ATOMIC))) { if (vcc->vci < 32) printk("Drop control packets\n"); - goto out_free_desc; + goto out_free_desc; } skb_put(skb,len); // pwang_test diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 74e18b0a6d89..3d7fb6516f74 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -805,7 +805,12 @@ static void solos_bh(unsigned long card_arg) continue; } - skb = alloc_skb(size + 1, GFP_ATOMIC); + /* Use netdev_alloc_skb() because it adds NET_SKB_PAD of + * headroom, and ensures we can route packets back out an + * Ethernet interface (for example) without having to + * reallocate. Adding NET_IP_ALIGN also ensures that both + * PPPoATM and PPPoEoBR2684 packets end up aligned. */ + skb = netdev_alloc_skb_ip_align(NULL, size + 1); if (!skb) { if (net_ratelimit()) dev_warn(&card->dev->dev, "Failed to allocate sk_buff for RX\n"); @@ -869,7 +874,10 @@ static void solos_bh(unsigned long card_arg) /* Allocate RX skbs for any ports which need them */ if (card->using_dma && card->atmdev[port] && !card->rx_skb[port]) { - struct sk_buff *skb = alloc_skb(RX_DMA_SIZE, GFP_ATOMIC); + /* Unlike the MMIO case (qv) we can't add NET_IP_ALIGN + * here; the FPGA can only DMA to addresses which are + * aligned to 4 bytes. */ + struct sk_buff *skb = dev_alloc_skb(RX_DMA_SIZE); if (skb) { SKB_CB(skb)->dma_addr = dma_map_single(&card->dev->dev, skb->data, diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 764280a91776..e9fd32e91668 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -148,7 +148,11 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) if (sibling == cpu) /* skip itself */ continue; + sib_cpu_ci = get_cpu_cacheinfo(sibling); + if (!sib_cpu_ci->info_list) + continue; + sib_leaf = sib_cpu_ci->info_list + index; cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); @@ -159,6 +163,9 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) static void free_cache_attributes(unsigned int cpu) { + if (!per_cpu_cacheinfo(cpu)) + return; + cache_shared_cpu_map_remove(cpu); kfree(per_cpu_cacheinfo(cpu)); @@ -514,8 +521,7 @@ static int cacheinfo_cpu_callback(struct notifier_block *nfb, break; case CPU_DEAD: cache_remove_dev(cpu); - if (per_cpu_cacheinfo(cpu)) - free_cache_attributes(cpu); + free_cache_attributes(cpu); break; } return notifier_from_errno(rc); diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index 1857a5dd0816..134483daac25 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -63,20 +63,8 @@ static int platform_msi_init(struct irq_domain *domain, unsigned int virq, irq_hw_number_t hwirq, msi_alloc_info_t *arg) { - struct irq_data *data; - - irq_domain_set_hwirq_and_chip(domain, virq, hwirq, - info->chip, info->chip_data); - - /* - * Save the MSI descriptor in handler_data so that the - * irq_write_msi_msg callback can retrieve it (and the - * associated device). - */ - data = irq_domain_get_irq_data(domain, virq); - data->handler_data = arg->desc; - - return 0; + return irq_domain_set_hwirq_and_chip(domain, virq, hwirq, + info->chip, info->chip_data); } #else #define platform_msi_set_desc NULL @@ -97,7 +85,7 @@ static void platform_msi_update_dom_ops(struct msi_domain_info *info) static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg) { - struct msi_desc *desc = irq_data_get_irq_handler_data(data); + struct msi_desc *desc = irq_data_get_msi_desc(data); struct platform_msi_priv_data *priv_data; priv_data = desc->platform.msi_priv_data; diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 416720159e96..16550c63d611 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -213,6 +213,18 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed) } /** + * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). + * @genpd: PM domait to power off. + * + * Queue up the execution of pm_genpd_poweroff() unless it's already been done + * before. + */ +static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) +{ + queue_work(pm_wq, &genpd->power_off_work); +} + +/** * __pm_genpd_poweron - Restore power to a given PM domain and its masters. * @genpd: PM domain to power up. * @@ -259,8 +271,12 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) return 0; err: - list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node) + list_for_each_entry_continue_reverse(link, + &genpd->slave_links, + slave_node) { genpd_sd_counter_dec(link->master); + genpd_queue_power_off_work(link->master); + } return ret; } @@ -349,18 +365,6 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, } /** - * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). - * @genpd: PM domait to power off. - * - * Queue up the execution of pm_genpd_poweroff() unless it's already been done - * before. - */ -static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) -{ - queue_work(pm_wq, &genpd->power_off_work); -} - -/** * pm_genpd_poweroff - Remove power from a given PM domain. * @genpd: PM domain to power down. * @@ -1469,6 +1473,13 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, mutex_lock(&genpd->lock); + if (!list_empty(&subdomain->slave_links) || subdomain->device_count) { + pr_warn("%s: unable to remove subdomain %s\n", genpd->name, + subdomain->name); + ret = -EBUSY; + goto out; + } + list_for_each_entry(link, &genpd->master_links, master_node) { if (link->slave != subdomain) continue; @@ -1487,6 +1498,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, break; } +out: mutex_unlock(&genpd->lock); return ret; diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index eb254497a494..7ae7cd990fbf 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -341,6 +341,34 @@ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); /** + * dev_pm_opp_get_suspend_opp() - Get suspend opp + * @dev: device for which we do this operation + * + * Return: This function returns pointer to the suspend opp if it is + * defined and available, otherwise it returns NULL. + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. + */ +struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev) +{ + struct device_opp *dev_opp; + + opp_rcu_lockdep_assert(); + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp) || !dev_opp->suspend_opp || + !dev_opp->suspend_opp->available) + return NULL; + + return dev_opp->suspend_opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp); + +/** * dev_pm_opp_get_opp_count() - Get number of opps available in the opp list * @dev: device for which we do this operation * @@ -864,10 +892,17 @@ static int opp_get_microvolt(struct dev_pm_opp *opp, struct device *dev) u32 microvolt[3] = {0}; int count, ret; - count = of_property_count_u32_elems(opp->np, "opp-microvolt"); - if (!count) + /* Missing property isn't a problem, but an invalid entry is */ + if (!of_find_property(opp->np, "opp-microvolt", NULL)) return 0; + count = of_property_count_u32_elems(opp->np, "opp-microvolt"); + if (count < 0) { + dev_err(dev, "%s: Invalid opp-microvolt property (%d)\n", + __func__, count); + return count; + } + /* There can be one or three elements here */ if (count != 1 && count != 3) { dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n", @@ -1035,7 +1070,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add); * share a common logic which is isolated here. * * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the - * copy operation, returns 0 if no modifcation was done OR modification was + * copy operation, returns 0 if no modification was done OR modification was * successful. * * Locking: The internal device_opp and opp structures are RCU protected. @@ -1123,7 +1158,7 @@ unlock: * mutex locking or synchronize_rcu() blocking calls cannot be used. * * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the - * copy operation, returns 0 if no modifcation was done OR modification was + * copy operation, returns 0 if no modification was done OR modification was * successful. */ int dev_pm_opp_enable(struct device *dev, unsigned long freq) @@ -1149,7 +1184,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_enable); * mutex locking or synchronize_rcu() blocking calls cannot be used. * * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the - * copy operation, returns 0 if no modifcation was done OR modification was + * copy operation, returns 0 if no modification was done OR modification was * successful. */ int dev_pm_opp_disable(struct device *dev, unsigned long freq) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index cc557886ab23..628ad7ac078b 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -98,6 +98,8 @@ struct regmap { int (*reg_read)(void *context, unsigned int reg, unsigned int *val); int (*reg_write)(void *context, unsigned int reg, unsigned int val); + int (*reg_update_bits)(void *context, unsigned int reg, + unsigned int mask, unsigned int val); bool defer_caching; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index afaf56200674..8cd155af3d63 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -619,6 +619,7 @@ struct regmap *__regmap_init(struct device *dev, goto skip_format_initialization; } else { map->reg_read = _regmap_bus_read; + map->reg_update_bits = bus->reg_update_bits; } reg_endian = regmap_get_reg_endian(bus, config); @@ -2509,20 +2510,26 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, int ret; unsigned int tmp, orig; - ret = _regmap_read(map, reg, &orig); - if (ret != 0) - return ret; + if (change) + *change = false; - tmp = orig & ~mask; - tmp |= val & mask; - - if (force_write || (tmp != orig)) { - ret = _regmap_write(map, reg, tmp); - if (change) + if (regmap_volatile(map, reg) && map->reg_update_bits) { + ret = map->reg_update_bits(map->bus_context, reg, mask, val); + if (ret == 0 && change) *change = true; } else { - if (change) - *change = false; + ret = _regmap_read(map, reg, &orig); + if (ret != 0) + return ret; + + tmp = orig & ~mask; + tmp |= val & mask; + + if (force_write || (tmp != orig)) { + ret = _regmap_write(map, reg, tmp); + if (ret == 0 && change) + *change = true; + } } return ret; diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 24882c18fcbe..59d8d0d14824 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -436,13 +436,8 @@ int bcma_bus_register(struct bcma_bus *bus) } dev = bcma_bus_get_host_dev(bus); - /* TODO: remove check for IS_BUILTIN(CONFIG_BCMA) check when - * of_default_bus_match_table is exported or in some other way - * accessible. This is just a temporary workaround. - */ - if (IS_BUILTIN(CONFIG_BCMA) && dev) { - of_platform_populate(dev->of_node, of_default_bus_match_table, - NULL, dev); + if (dev) { + of_platform_default_populate(dev->of_node, NULL, dev); } /* Cores providing flash access go before SPROM init */ diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c index 17269a3b85f2..a295b98c6bae 100644 --- a/drivers/block/null_blk.c +++ b/drivers/block/null_blk.c @@ -406,6 +406,22 @@ static struct blk_mq_ops null_mq_ops = { .complete = null_softirq_done_fn, }; +static void cleanup_queue(struct nullb_queue *nq) +{ + kfree(nq->tag_map); + kfree(nq->cmds); +} + +static void cleanup_queues(struct nullb *nullb) +{ + int i; + + for (i = 0; i < nullb->nr_queues; i++) + cleanup_queue(&nullb->queues[i]); + + kfree(nullb->queues); +} + static void null_del_dev(struct nullb *nullb) { list_del_init(&nullb->list); @@ -415,6 +431,7 @@ static void null_del_dev(struct nullb *nullb) if (queue_mode == NULL_Q_MQ) blk_mq_free_tag_set(&nullb->tag_set); put_disk(nullb->disk); + cleanup_queues(nullb); kfree(nullb); } @@ -459,22 +476,6 @@ static int setup_commands(struct nullb_queue *nq) return 0; } -static void cleanup_queue(struct nullb_queue *nq) -{ - kfree(nq->tag_map); - kfree(nq->cmds); -} - -static void cleanup_queues(struct nullb *nullb) -{ - int i; - - for (i = 0; i < nullb->nr_queues; i++) - cleanup_queue(&nullb->queues[i]); - - kfree(nullb->queues); -} - static int setup_queues(struct nullb *nullb) { nullb->queues = kzalloc(submit_queues * sizeof(struct nullb_queue), @@ -588,8 +589,7 @@ static int null_add_dev(void) blk_queue_physical_block_size(nullb->q, bs); size = gb * 1024 * 1024 * 1024ULL; - sector_div(size, bs); - set_capacity(disk, size); + set_capacity(disk, size >> 9); disk->flags |= GENHD_FL_EXT_DEVT | GENHD_FL_SUPPRESS_PARTITION_INFO; disk->major = null_major; diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 698f761037ce..d93a0372b37b 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -4673,7 +4673,10 @@ static int rbd_dev_v2_header_info(struct rbd_device *rbd_dev) } ret = rbd_dev_v2_snap_context(rbd_dev); - dout("rbd_dev_v2_snap_context returned %d\n", ret); + if (ret && first_time) { + kfree(rbd_dev->header.object_prefix); + rbd_dev->header.object_prefix = NULL; + } return ret; } @@ -5154,7 +5157,6 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev) out_err: if (parent) { rbd_dev_unparent(rbd_dev); - kfree(rbd_dev->header_name); rbd_dev_destroy(parent); } else { rbd_put_client(rbdc); diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index 965d1afb0eaa..5cb13ca3a3ac 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -330,12 +330,14 @@ void zcomp_destroy(struct zcomp *comp) * allocate new zcomp and initialize it. return compressing * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in - * case of allocation error. + * case of allocation error, or any other error potentially + * returned by functions zcomp_strm_{multi,single}_create. */ struct zcomp *zcomp_create(const char *compress, int max_strm) { struct zcomp *comp; struct zcomp_backend *backend; + int error; backend = find_backend(compress); if (!backend) @@ -347,12 +349,12 @@ struct zcomp *zcomp_create(const char *compress, int max_strm) comp->backend = backend; if (max_strm > 1) - zcomp_strm_multi_create(comp, max_strm); + error = zcomp_strm_multi_create(comp, max_strm); else - zcomp_strm_single_create(comp); - if (!comp->stream) { + error = zcomp_strm_single_create(comp); + if (error) { kfree(comp); - return ERR_PTR(-ENOMEM); + return ERR_PTR(error); } return comp; } diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 0bd88c942a52..c9c5dd0bad36 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -4,6 +4,7 @@ menu "Bluetooth device drivers" config BT_INTEL tristate + select REGMAP config BT_BCM tristate @@ -183,6 +184,7 @@ config BT_HCIBCM203X config BT_HCIBPA10X tristate "HCI BPA10x USB driver" depends on USB + select BT_HCIUART_H4 help Bluetooth HCI BPA10x USB driver. This driver provides support for the Digianswer BPA 100/105 Bluetooth @@ -275,7 +277,7 @@ config BT_MRVL The core driver to support Marvell Bluetooth devices. This driver is required if you want to support - Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897. + Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897/8997. Say Y here to compile Marvell Bluetooth driver into the kernel or say M to compile it as module. @@ -289,7 +291,7 @@ config BT_MRVL_SDIO The driver for Marvell Bluetooth chipsets with SDIO interface. This driver is required if you want to use Marvell Bluetooth - devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897 + devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897/SD8997 chipsets are supported. Say Y here to compile support for Marvell BT-over-SDIO driver diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index a5c4d0584389..616ec2ac1b22 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -422,17 +422,12 @@ static int bfusb_open(struct hci_dev *hdev) BT_DBG("hdev %p bfusb %p", hdev, data); - if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) - return 0; - write_lock_irqsave(&data->lock, flags); err = bfusb_rx_submit(data, NULL); if (!err) { for (i = 1; i < BFUSB_MAX_BULK_RX; i++) bfusb_rx_submit(data, NULL); - } else { - clear_bit(HCI_RUNNING, &hdev->flags); } write_unlock_irqrestore(&data->lock, flags); @@ -458,9 +453,6 @@ static int bfusb_close(struct hci_dev *hdev) BT_DBG("hdev %p bfusb %p", hdev, data); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - write_lock_irqsave(&data->lock, flags); write_unlock_irqrestore(&data->lock, flags); @@ -479,9 +471,6 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 35e63aaa6f80..36fa1c958c74 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -390,7 +390,7 @@ static void bluecard_receive(struct bluecard_info *info, for (i = 0; i < len; i++) { /* Allocate packet */ - if (info->rx_skb == NULL) { + if (!info->rx_skb) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); @@ -628,9 +628,6 @@ static int bluecard_hci_open(struct hci_dev *hdev) if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE); - if (test_and_set_bit(HCI_RUNNING, &(hdev->flags))) - return 0; - if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) { unsigned int iobase = info->p_dev->resource[0]->start; @@ -646,9 +643,6 @@ static int bluecard_hci_close(struct hci_dev *hdev) { struct bluecard_info *info = hci_get_drvdata(hdev); - if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) - return 0; - bluecard_hci_flush(hdev); if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) { diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 8a319913c9a9..49c397e21b39 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -35,7 +35,9 @@ #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> -#define VERSION "0.10" +#include "hci_uart.h" + +#define VERSION "0.11" static const struct usb_device_id bpa10x_table[] = { /* Tektronix BPA 100/105 (Digianswer) */ @@ -56,112 +58,6 @@ struct bpa10x_data { struct sk_buff *rx_skb[2]; }; -#define HCI_VENDOR_HDR_SIZE 5 - -struct hci_vendor_hdr { - __u8 type; - __le16 snum; - __le16 dlen; -} __packed; - -static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count) -{ - struct bpa10x_data *data = hci_get_drvdata(hdev); - - BT_DBG("%s queue %d buffer %p count %d", hdev->name, - queue, buf, count); - - if (queue < 0 || queue > 1) - return -EILSEQ; - - hdev->stat.byte_rx += count; - - while (count) { - struct sk_buff *skb = data->rx_skb[queue]; - struct { __u8 type; int expect; } *scb; - int type, len = 0; - - if (!skb) { - /* Start of the frame */ - - type = *((__u8 *) buf); - count--; buf++; - - switch (type) { - case HCI_EVENT_PKT: - if (count >= HCI_EVENT_HDR_SIZE) { - struct hci_event_hdr *h = buf; - len = HCI_EVENT_HDR_SIZE + h->plen; - } else - return -EILSEQ; - break; - - case HCI_ACLDATA_PKT: - if (count >= HCI_ACL_HDR_SIZE) { - struct hci_acl_hdr *h = buf; - len = HCI_ACL_HDR_SIZE + - __le16_to_cpu(h->dlen); - } else - return -EILSEQ; - break; - - case HCI_SCODATA_PKT: - if (count >= HCI_SCO_HDR_SIZE) { - struct hci_sco_hdr *h = buf; - len = HCI_SCO_HDR_SIZE + h->dlen; - } else - return -EILSEQ; - break; - - case HCI_VENDOR_PKT: - if (count >= HCI_VENDOR_HDR_SIZE) { - struct hci_vendor_hdr *h = buf; - len = HCI_VENDOR_HDR_SIZE + - __le16_to_cpu(h->dlen); - } else - return -EILSEQ; - break; - } - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) { - BT_ERR("%s no memory for packet", hdev->name); - return -ENOMEM; - } - - data->rx_skb[queue] = skb; - - scb = (void *) skb->cb; - scb->type = type; - scb->expect = len; - } else { - /* Continuation */ - - scb = (void *) skb->cb; - len = scb->expect; - } - - len = min(len, count); - - memcpy(skb_put(skb, len), buf, len); - - scb->expect -= len; - - if (scb->expect == 0) { - /* Complete frame */ - - data->rx_skb[queue] = NULL; - - bt_cb(skb)->pkt_type = scb->type; - hci_recv_frame(hdev, skb); - } - - count -= len; buf += len; - } - - return 0; -} - static void bpa10x_tx_complete(struct urb *urb) { struct sk_buff *skb = urb->context; @@ -184,6 +80,22 @@ done: kfree_skb(skb); } +#define HCI_VENDOR_HDR_SIZE 5 + +#define HCI_RECV_VENDOR \ + .type = HCI_VENDOR_PKT, \ + .hlen = HCI_VENDOR_HDR_SIZE, \ + .loff = 3, \ + .lsize = 2, \ + .maxlen = HCI_MAX_FRAME_SIZE + +static const struct h4_recv_pkt bpa10x_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, + { HCI_RECV_VENDOR, .recv = hci_recv_diag }, +}; + static void bpa10x_rx_complete(struct urb *urb) { struct hci_dev *hdev = urb->context; @@ -197,11 +109,17 @@ static void bpa10x_rx_complete(struct urb *urb) return; if (urb->status == 0) { - if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe), + bool idx = usb_pipebulk(urb->pipe); + + data->rx_skb[idx] = h4_recv_buf(hdev, data->rx_skb[idx], urb->transfer_buffer, - urb->actual_length) < 0) { + urb->actual_length, + bpa10x_recv_pkts, + ARRAY_SIZE(bpa10x_recv_pkts)); + if (IS_ERR(data->rx_skb[idx])) { BT_ERR("%s corrupted event packet", hdev->name); hdev->stat.err_rx++; + data->rx_skb[idx] = NULL; } } @@ -304,9 +222,6 @@ static int bpa10x_open(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) - return 0; - err = bpa10x_submit_intr_urb(hdev); if (err < 0) goto error; @@ -320,8 +235,6 @@ static int bpa10x_open(struct hci_dev *hdev) error: usb_kill_anchored_urbs(&data->rx_anchor); - clear_bit(HCI_RUNNING, &hdev->flags); - return err; } @@ -331,9 +244,6 @@ static int bpa10x_close(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - usb_kill_anchored_urbs(&data->rx_anchor); return 0; @@ -350,6 +260,24 @@ static int bpa10x_flush(struct hci_dev *hdev) return 0; } +static int bpa10x_setup(struct hci_dev *hdev) +{ + const u8 req[] = { 0x07 }; + struct sk_buff *skb; + + BT_DBG("%s", hdev->name); + + /* Read revision string */ + skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); + + kfree_skb(skb); + return 0; +} + static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct bpa10x_data *data = hci_get_drvdata(hdev); @@ -360,9 +288,6 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - skb->dev = (void *) hdev; urb = usb_alloc_urb(0, GFP_ATOMIC); @@ -431,6 +356,25 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb) return 0; } +static int bpa10x_set_diag(struct hci_dev *hdev, bool enable) +{ + const u8 req[] = { 0x00, enable }; + struct sk_buff *skb; + + BT_DBG("%s", hdev->name); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -ENETDOWN; + + /* Enable sniffer operation */ + skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + kfree_skb(skb); + return 0; +} + static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct bpa10x_data *data; @@ -465,7 +409,9 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id * hdev->open = bpa10x_open; hdev->close = bpa10x_close; hdev->flush = bpa10x_flush; + hdev->setup = bpa10x_setup; hdev->send = bpa10x_send_frame; + hdev->set_diag = bpa10x_set_diag; set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index a00bb82eb7c6..5803aaed958f 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -233,7 +233,7 @@ static void bt3c_receive(struct bt3c_info *info) info->hdev->stat.byte_rx++; /* Allocate packet */ - if (info->rx_skb == NULL) { + if (!info->rx_skb) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); @@ -270,7 +270,6 @@ static void bt3c_receive(struct bt3c_info *info) /* Unknown packet */ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); info->hdev->stat.err_rx++; - clear_bit(HCI_RUNNING, &(info->hdev->flags)); kfree_skb(info->rx_skb); info->rx_skb = NULL; @@ -395,17 +394,12 @@ static int bt3c_hci_flush(struct hci_dev *hdev) static int bt3c_hci_open(struct hci_dev *hdev) { - set_bit(HCI_RUNNING, &(hdev->flags)); - return 0; } static int bt3c_hci_close(struct hci_dev *hdev) { - if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) - return 0; - bt3c_hci_flush(hdev); return 0; @@ -453,7 +447,8 @@ static int bt3c_load_firmware(struct bt3c_info *info, { char *ptr = (char *) firmware; char b[9]; - unsigned int iobase, size, addr, fcs, tmp; + unsigned int iobase, tmp; + unsigned long size, addr, fcs; int i, err = 0; iobase = info->p_dev->resource[0]->start; @@ -478,15 +473,18 @@ static int bt3c_load_firmware(struct bt3c_info *info, memset(b, 0, sizeof(b)); memcpy(b, ptr + 2, 2); - size = simple_strtoul(b, NULL, 16); + if (kstrtoul(b, 16, &size) < 0) + return -EINVAL; memset(b, 0, sizeof(b)); memcpy(b, ptr + 4, 8); - addr = simple_strtoul(b, NULL, 16); + if (kstrtoul(b, 16, &addr) < 0) + return -EINVAL; memset(b, 0, sizeof(b)); memcpy(b, ptr + (size * 2) + 2, 2); - fcs = simple_strtoul(b, NULL, 16); + if (kstrtoul(b, 16, &fcs) < 0) + return -EINVAL; memset(b, 0, sizeof(b)); for (tmp = 0, i = 0; i < size; i++) { diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 02ed816a18f9..2fc363a0393d 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -181,6 +181,27 @@ static int btbcm_reset(struct hci_dev *hdev) return 0; } +static struct sk_buff *btbcm_read_local_name(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: BCM: Reading local name failed (%ld)", + hdev->name, PTR_ERR(skb)); + return skb; + } + + if (skb->len != sizeof(struct hci_rp_read_local_name)) { + BT_ERR("%s: BCM: Local name length mismatch", hdev->name); + kfree_skb(skb); + return ERR_PTR(-EIO); + } + + return skb; +} + static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev) { struct sk_buff *skb; @@ -393,6 +414,14 @@ int btbcm_setup_patchram(struct hci_dev *hdev) BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]); kfree_skb(skb); + /* Read Local Name */ + skb = btbcm_read_local_name(hdev); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); + kfree_skb(skb); + switch ((rev & 0xf000) >> 12) { case 0: case 3: @@ -464,6 +493,14 @@ int btbcm_setup_patchram(struct hci_dev *hdev) hw_name ? : "BCM", (subver & 0x7000) >> 13, (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); + /* Read Local Name */ + skb = btbcm_read_local_name(hdev); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); + kfree_skb(skb); + btbcm_check_bdaddr(hdev); set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); @@ -475,12 +512,25 @@ EXPORT_SYMBOL_GPL(btbcm_setup_patchram); int btbcm_setup_apple(struct hci_dev *hdev) { struct sk_buff *skb; + int err; + + /* Reset */ + err = btbcm_reset(hdev); + if (err) + return err; /* Read Verbose Config Version Info */ skb = btbcm_read_verbose_config(hdev); if (!IS_ERR(skb)) { - BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1], - get_unaligned_le16(skb->data + 5)); + BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, + skb->data[1], get_unaligned_le16(skb->data + 5)); + kfree_skb(skb); + } + + /* Read Local Name */ + skb = btbcm_read_local_name(hdev); + if (!IS_ERR(skb)) { + BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); kfree_skb(skb); } diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 048423fd83bf..7047fe6a6a2b 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -22,6 +22,8 @@ */ #include <linux/module.h> +#include <linux/firmware.h> +#include <linux/regmap.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -169,6 +171,246 @@ int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen, } EXPORT_SYMBOL_GPL(btintel_secure_send); +int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name) +{ + const struct firmware *fw; + struct sk_buff *skb; + const u8 *fw_ptr; + int err; + + err = request_firmware_direct(&fw, ddc_name, &hdev->dev); + if (err < 0) { + bt_dev_err(hdev, "Failed to load Intel DDC file %s (%d)", + ddc_name, err); + return err; + } + + bt_dev_info(hdev, "Found Intel DDC parameters: %s", ddc_name); + + fw_ptr = fw->data; + + /* DDC file contains one or more DDC structure which has + * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2). + */ + while (fw->size > fw_ptr - fw->data) { + u8 cmd_plen = fw_ptr[0] + sizeof(u8); + + skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Failed to send Intel_Write_DDC (%ld)", + PTR_ERR(skb)); + release_firmware(fw); + return PTR_ERR(skb); + } + + fw_ptr += cmd_plen; + kfree_skb(skb); + } + + release_firmware(fw); + + bt_dev_info(hdev, "Applying Intel DDC parameters completed"); + + return 0; +} +EXPORT_SYMBOL_GPL(btintel_load_ddc_config); + +/* ------- REGMAP IBT SUPPORT ------- */ + +#define IBT_REG_MODE_8BIT 0x00 +#define IBT_REG_MODE_16BIT 0x01 +#define IBT_REG_MODE_32BIT 0x02 + +struct regmap_ibt_context { + struct hci_dev *hdev; + __u16 op_write; + __u16 op_read; +}; + +struct ibt_cp_reg_access { + __le32 addr; + __u8 mode; + __u8 len; + __u8 data[0]; +} __packed; + +struct ibt_rp_reg_access { + __u8 status; + __le32 addr; + __u8 data[0]; +} __packed; + +static int regmap_ibt_read(void *context, const void *addr, size_t reg_size, + void *val, size_t val_size) +{ + struct regmap_ibt_context *ctx = context; + struct ibt_cp_reg_access cp; + struct ibt_rp_reg_access *rp; + struct sk_buff *skb; + int err = 0; + + if (reg_size != sizeof(__le32)) + return -EINVAL; + + switch (val_size) { + case 1: + cp.mode = IBT_REG_MODE_8BIT; + break; + case 2: + cp.mode = IBT_REG_MODE_16BIT; + break; + case 4: + cp.mode = IBT_REG_MODE_32BIT; + break; + default: + return -EINVAL; + } + + /* regmap provides a little-endian formatted addr */ + cp.addr = *(__le32 *)addr; + cp.len = val_size; + + bt_dev_dbg(ctx->hdev, "Register (0x%x) read", le32_to_cpu(cp.addr)); + + skb = hci_cmd_sync(ctx->hdev, ctx->op_read, sizeof(cp), &cp, + HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error (%d)", + le32_to_cpu(cp.addr), err); + return err; + } + + if (skb->len != sizeof(*rp) + val_size) { + bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad len", + le32_to_cpu(cp.addr)); + err = -EINVAL; + goto done; + } + + rp = (struct ibt_rp_reg_access *)skb->data; + + if (rp->addr != cp.addr) { + bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad addr", + le32_to_cpu(rp->addr)); + err = -EINVAL; + goto done; + } + + memcpy(val, rp->data, val_size); + +done: + kfree_skb(skb); + return err; +} + +static int regmap_ibt_gather_write(void *context, + const void *addr, size_t reg_size, + const void *val, size_t val_size) +{ + struct regmap_ibt_context *ctx = context; + struct ibt_cp_reg_access *cp; + struct sk_buff *skb; + int plen = sizeof(*cp) + val_size; + u8 mode; + int err = 0; + + if (reg_size != sizeof(__le32)) + return -EINVAL; + + switch (val_size) { + case 1: + mode = IBT_REG_MODE_8BIT; + break; + case 2: + mode = IBT_REG_MODE_16BIT; + break; + case 4: + mode = IBT_REG_MODE_32BIT; + break; + default: + return -EINVAL; + } + + cp = kmalloc(plen, GFP_KERNEL); + if (!cp) + return -ENOMEM; + + /* regmap provides a little-endian formatted addr/value */ + cp->addr = *(__le32 *)addr; + cp->mode = mode; + cp->len = val_size; + memcpy(&cp->data, val, val_size); + + bt_dev_dbg(ctx->hdev, "Register (0x%x) write", le32_to_cpu(cp->addr)); + + skb = hci_cmd_sync(ctx->hdev, ctx->op_write, plen, cp, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(ctx->hdev, "regmap: Register (0x%x) write error (%d)", + le32_to_cpu(cp->addr), err); + goto done; + } + kfree_skb(skb); + +done: + kfree(cp); + return err; +} + +static int regmap_ibt_write(void *context, const void *data, size_t count) +{ + /* data contains register+value, since we only support 32bit addr, + * minimum data size is 4 bytes. + */ + if (WARN_ONCE(count < 4, "Invalid register access")) + return -EINVAL; + + return regmap_ibt_gather_write(context, data, 4, data + 4, count - 4); +} + +static void regmap_ibt_free_context(void *context) +{ + kfree(context); +} + +static struct regmap_bus regmap_ibt = { + .read = regmap_ibt_read, + .write = regmap_ibt_write, + .gather_write = regmap_ibt_gather_write, + .free_context = regmap_ibt_free_context, + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, +}; + +/* Config is the same for all register regions */ +static const struct regmap_config regmap_ibt_cfg = { + .name = "btintel_regmap", + .reg_bits = 32, + .val_bits = 32, +}; + +struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read, + u16 opcode_write) +{ + struct regmap_ibt_context *ctx; + + bt_dev_info(hdev, "regmap: Init R%x-W%x region", opcode_read, + opcode_write); + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->op_read = opcode_read; + ctx->op_write = opcode_write; + ctx->hdev = hdev; + + return regmap_init(&hdev->dev, ®map_ibt, ctx, ®map_ibt_cfg); +} +EXPORT_SYMBOL_GPL(btintel_regmap_init); + MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION); MODULE_VERSION(VERSION); diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index b278d14758d5..f0655c476fd2 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -78,6 +78,10 @@ void btintel_hw_error(struct hci_dev *hdev, u8 code); void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver); int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen, const void *param); +int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name); + +struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read, + u16 opcode_write); #else @@ -95,7 +99,8 @@ static inline void btintel_hw_error(struct hci_dev *hdev, u8 code) { } -static void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver) +static inline void btintel_version_info(struct hci_dev *hdev, + struct intel_version *ver) { } @@ -105,4 +110,16 @@ static inline int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, return -EOPNOTSUPP; } +static inline int btintel_load_ddc_config(struct hci_dev *hdev, + const char *ddc_name) +{ + return -EOPNOTSUPP; +} + +static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev, + u16 opcode_read, + u16 opcode_write) +{ + return ERR_PTR(-EINVAL); +} #endif diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index de05deb444ce..6ba22862d788 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -184,7 +184,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode, } skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC); - if (skb == NULL) { + if (!skb) { BT_ERR("No free skb"); return -ENOMEM; } @@ -377,20 +377,6 @@ static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) return -EINVAL; } - if (skb_headroom(skb) < BTM_HEADER_LEN) { - struct sk_buff *tmp = skb; - - skb = skb_realloc_headroom(skb, BTM_HEADER_LEN); - if (!skb) { - BT_ERR("Tx Error: realloc_headroom failed %d", - BTM_HEADER_LEN); - skb = tmp; - return -EINVAL; - } - - kfree_skb(tmp); - } - skb_push(skb, BTM_HEADER_LEN); /* header type: byte[3] @@ -450,13 +436,6 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len); - if (!test_bit(HCI_RUNNING, &hdev->flags)) { - BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags); - print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET, - skb->data, skb->len); - return -EBUSY; - } - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; @@ -491,9 +470,6 @@ static int btmrvl_close(struct hci_dev *hdev) { struct btmrvl_private *priv = hci_get_drvdata(hdev); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - skb_queue_purge(&priv->adapter->tx_queue); return 0; @@ -501,8 +477,6 @@ static int btmrvl_close(struct hci_dev *hdev) static int btmrvl_open(struct hci_dev *hdev) { - set_bit(HCI_RUNNING, &hdev->flags); - return 0; } diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index b9978a7ba0cc..71ea2a3af293 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -146,6 +146,29 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = { .fw_dump_end = 0xea, }; +static const struct btmrvl_sdio_card_reg btmrvl_reg_8997 = { + .cfg = 0x00, + .host_int_mask = 0x08, + .host_intstatus = 0x0c, + .card_status = 0x5c, + .sq_read_base_addr_a0 = 0xf8, + .sq_read_base_addr_a1 = 0xf9, + .card_revision = 0xc8, + .card_fw_status0 = 0xe8, + .card_fw_status1 = 0xe9, + .card_rx_len = 0xea, + .card_rx_unit = 0xeb, + .io_port_0 = 0xe4, + .io_port_1 = 0xe5, + .io_port_2 = 0xe6, + .int_read_to_clear = true, + .host_int_rsr = 0x04, + .card_misc_cfg = 0xD8, + .fw_dump_ctrl = 0xf0, + .fw_dump_start = 0xf1, + .fw_dump_end = 0xf8, +}; + static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { .helper = "mrvl/sd8688_helper.bin", .firmware = "mrvl/sd8688.bin", @@ -191,25 +214,37 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { .supports_fw_dump = true, }; +static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = { + .helper = NULL, + .firmware = "mrvl/sd8997_uapsta.bin", + .reg = &btmrvl_reg_8997, + .support_pscan_win_report = true, + .sd_blksz_fw_dl = 256, + .supports_fw_dump = true, +}; + static const struct sdio_device_id btmrvl_sdio_ids[] = { /* Marvell SD8688 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105), - .driver_data = (unsigned long) &btmrvl_sdio_sd8688 }, + .driver_data = (unsigned long)&btmrvl_sdio_sd8688 }, /* Marvell SD8787 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A), - .driver_data = (unsigned long) &btmrvl_sdio_sd8787 }, + .driver_data = (unsigned long)&btmrvl_sdio_sd8787 }, /* Marvell SD8787 Bluetooth AMP device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B), - .driver_data = (unsigned long) &btmrvl_sdio_sd8787 }, + .driver_data = (unsigned long)&btmrvl_sdio_sd8787 }, /* Marvell SD8797 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A), - .driver_data = (unsigned long) &btmrvl_sdio_sd8797 }, + .driver_data = (unsigned long)&btmrvl_sdio_sd8797 }, /* Marvell SD8887 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136), .driver_data = (unsigned long)&btmrvl_sdio_sd8887 }, /* Marvell SD8897 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E), - .driver_data = (unsigned long) &btmrvl_sdio_sd8897 }, + .driver_data = (unsigned long)&btmrvl_sdio_sd8897 }, + /* Marvell SD8997 Bluetooth device */ + { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9142), + .driver_data = (unsigned long)&btmrvl_sdio_sd8997 }, { } /* Terminating entry */ }; @@ -619,7 +654,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) /* Allocate buffer */ skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC); - if (skb == NULL) { + if (!skb) { BT_ERR("No free skb"); ret = -ENOMEM; goto exit; @@ -1278,6 +1313,12 @@ static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv) if (memory_size == 0) { BT_INFO("Firmware dump finished!"); + sdio_writeb(card->func, FW_DUMP_READ_DONE, + card->reg->fw_dump_ctrl, &ret); + if (ret) { + BT_ERR("SDIO Write MEMDUMP_FINISH ERR"); + goto done; + } break; } @@ -1616,3 +1657,4 @@ MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin"); +MODULE_FIRMWARE("mrvl/sd8997_uapsta.bin"); diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index 83f6437dd91d..7b624423a7e8 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -194,21 +194,15 @@ static int btsdio_open(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) - return 0; - sdio_claim_host(data->func); err = sdio_enable_func(data->func); - if (err < 0) { - clear_bit(HCI_RUNNING, &hdev->flags); + if (err < 0) goto release; - } err = sdio_claim_irq(data->func, btsdio_interrupt); if (err < 0) { sdio_disable_func(data->func); - clear_bit(HCI_RUNNING, &hdev->flags); goto release; } @@ -229,9 +223,6 @@ static int btsdio_close(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - sdio_claim_host(data->func); sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL); @@ -261,9 +252,6 @@ static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index abb4d2106db4..bb8e4025fb9e 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -38,7 +38,7 @@ #include <linux/serial.h> #include <linux/serial_reg.h> #include <linux/bitops.h> -#include <asm/io.h> +#include <linux/io.h> #include <pcmcia/cistpl.h> #include <pcmcia/ciscode.h> @@ -188,7 +188,7 @@ static void btuart_receive(struct btuart_info *info) info->hdev->stat.byte_rx++; /* Allocate packet */ - if (info->rx_skb == NULL) { + if (!info->rx_skb) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); @@ -223,7 +223,6 @@ static void btuart_receive(struct btuart_info *info) /* Unknown packet */ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); info->hdev->stat.err_rx++; - clear_bit(HCI_RUNNING, &(info->hdev->flags)); kfree_skb(info->rx_skb); info->rx_skb = NULL; @@ -409,17 +408,12 @@ static int btuart_hci_flush(struct hci_dev *hdev) static int btuart_hci_open(struct hci_dev *hdev) { - set_bit(HCI_RUNNING, &(hdev->flags)); - return 0; } static int btuart_hci_close(struct hci_dev *hdev) { - if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) - return 0; - btuart_hci_flush(hdev); return 0; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b6aceaf82aa8..247b1062cb9a 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -940,9 +940,6 @@ static int btusb_open(struct hci_dev *hdev) data->intf->needs_remote_wakeup = 1; - if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) - goto done; - if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)) goto done; @@ -965,7 +962,6 @@ done: failed: clear_bit(BTUSB_INTR_RUNNING, &data->flags); - clear_bit(HCI_RUNNING, &hdev->flags); usb_autopm_put_interface(data->intf); return err; } @@ -984,9 +980,6 @@ static int btusb_close(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - cancel_work_sync(&data->work); cancel_work_sync(&data->waker); @@ -1156,9 +1149,6 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: urb = alloc_ctrl_urb(hdev, skb); @@ -1277,6 +1267,20 @@ static void btusb_work(struct work_struct *work) clear_bit(BTUSB_ISOC_RUNNING, &data->flags); usb_kill_anchored_urbs(&data->isoc_anchor); + /* When isochronous alternate setting needs to be + * changed, because SCO connection has been added + * or removed, a packet fragment may be left in the + * reassembling state. This could lead to wrongly + * assembled fragments. + * + * Clear outstanding fragment when selecting a new + * alternate setting. + */ + spin_lock(&data->rxlock); + kfree_skb(data->sco_skb); + data->sco_skb = NULL; + spin_unlock(&data->rxlock); + if (__set_isoc_interface(hdev, new_alts) < 0) return; } @@ -1348,7 +1352,9 @@ static int btusb_setup_csr(struct hci_dev *hdev) rp = (struct hci_rp_read_local_version *)skb->data; - if (le16_to_cpu(rp->manufacturer) != 10) { + /* Detect controllers which aren't real CSR ones. */ + if (le16_to_cpu(rp->manufacturer) != 10 || + le16_to_cpu(rp->lmp_subver) == 0x0c5c) { /* Clear the reset quirk since this is not an actual * early Bluetooth 1.1 device from CSR. */ @@ -1827,9 +1833,6 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: if (test_bit(BTUSB_BOOTLOADER, &data->flags)) { @@ -2217,36 +2220,7 @@ done: * The device can work without DDC parameters, so even if it fails * to load the file, no need to fail the setup. */ - err = request_firmware_direct(&fw, fwname, &hdev->dev); - if (err < 0) - return 0; - - BT_INFO("%s: Found Intel DDC parameters: %s", hdev->name, fwname); - - fw_ptr = fw->data; - - /* DDC file contains one or more DDC structure which has - * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2). - */ - while (fw->size > fw_ptr - fw->data) { - u8 cmd_plen = fw_ptr[0] + sizeof(u8); - - skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr, - HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - BT_ERR("%s: Failed to send Intel_Write_DDC (%ld)", - hdev->name, PTR_ERR(skb)); - release_firmware(fw); - return PTR_ERR(skb); - } - - fw_ptr += cmd_plen; - kfree_skb(skb); - } - - release_firmware(fw); - - BT_INFO("%s: Applying Intel DDC parameters completed", hdev->name); + btintel_load_ddc_config(hdev, fwname); return 0; } @@ -2782,7 +2756,7 @@ static int btusb_probe(struct usb_interface *intf, set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); /* Fake CSR devices with broken commands */ - if (bcdDevice <= 0x100) + if (bcdDevice <= 0x100 || bcdDevice == 0x134) hdev->setup = btusb_setup_csr; set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c index 7a722df97343..57eb935aedc7 100644 --- a/drivers/bluetooth/btwilink.c +++ b/drivers/bluetooth/btwilink.c @@ -155,9 +155,6 @@ static int ti_st_open(struct hci_dev *hdev) BT_DBG("%s %p", hdev->name, hdev); - if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - /* provide contexts for callbacks from ST */ hst = hci_get_drvdata(hdev); @@ -181,7 +178,6 @@ static int ti_st_open(struct hci_dev *hdev) goto done; if (err != -EINPROGRESS) { - clear_bit(HCI_RUNNING, &hdev->flags); BT_ERR("st_register failed %d", err); return err; } @@ -195,7 +191,6 @@ static int ti_st_open(struct hci_dev *hdev) (&hst->wait_reg_completion, msecs_to_jiffies(BT_REGISTER_TIMEOUT)); if (!timeleft) { - clear_bit(HCI_RUNNING, &hdev->flags); BT_ERR("Timeout(%d sec),didn't get reg " "completion signal from ST", BT_REGISTER_TIMEOUT / 1000); @@ -205,7 +200,6 @@ static int ti_st_open(struct hci_dev *hdev) /* Is ST registration callback * called with ERROR status? */ if (hst->reg_status != 0) { - clear_bit(HCI_RUNNING, &hdev->flags); BT_ERR("ST registration completed with invalid " "status %d", hst->reg_status); return -EAGAIN; @@ -215,7 +209,6 @@ done: hst->st_write = ti_st_proto[i].write; if (!hst->st_write) { BT_ERR("undefined ST write function"); - clear_bit(HCI_RUNNING, &hdev->flags); for (i = 0; i < MAX_BT_CHNL_IDS; i++) { /* Undo registration with ST */ err = st_unregister(&ti_st_proto[i]); @@ -236,9 +229,6 @@ static int ti_st_close(struct hci_dev *hdev) int err, i; struct ti_st *hst = hci_get_drvdata(hdev); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) { err = st_unregister(&ti_st_proto[i]); if (err) @@ -256,9 +246,6 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb) struct ti_st *hst; long len; - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - hst = hci_get_drvdata(hdev); /* Prepend skb with frame type */ diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 84135c54ed2e..5026f66fac88 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -357,8 +357,6 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst) static int dtl1_hci_open(struct hci_dev *hdev) { - set_bit(HCI_RUNNING, &(hdev->flags)); - return 0; } @@ -376,9 +374,6 @@ static int dtl1_hci_flush(struct hci_dev *hdev) static int dtl1_hci_close(struct hci_dev *hdev) { - if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) - return 0; - dtl1_hci_flush(hdev); return 0; diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 835bfab88ef5..645e66e9a945 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -31,6 +31,9 @@ #include <linux/clk.h> #include <linux/gpio/consumer.h> #include <linux/tty.h> +#include <linux/interrupt.h> +#include <linux/dmi.h> +#include <linux/pm_runtime.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -38,6 +41,11 @@ #include "btbcm.h" #include "hci_uart.h" +#define BCM_LM_DIAG_PKT 0x07 +#define BCM_LM_DIAG_SIZE 63 + +#define BCM_AUTOSUSPEND_DELAY 5000 /* default autosleep delay */ + struct bcm_device { struct list_head list; @@ -51,8 +59,10 @@ struct bcm_device { bool clk_enabled; u32 init_speed; + int irq; + u8 irq_polarity; -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM struct hci_uart *hu; bool is_suspended; /* suspend/resume flag */ #endif @@ -66,7 +76,7 @@ struct bcm_data { }; /* List of BCM BT UART devices */ -static DEFINE_SPINLOCK(bcm_device_lock); +static DEFINE_MUTEX(bcm_device_lock); static LIST_HEAD(bcm_device_list); static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed) @@ -80,7 +90,7 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed) clock.type = BCM_UART_CLOCK_48MHZ; - BT_DBG("%s: Set Controller clock (%d)", hdev->name, clock.type); + bt_dev_dbg(hdev, "Set Controller clock (%d)", clock.type); /* This Broadcom specific command changes the UART's controller * clock for baud rate > 3000000. @@ -88,15 +98,15 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed) skb = __hci_cmd_sync(hdev, 0xfc45, 1, &clock, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); - BT_ERR("%s: BCM: failed to write clock command (%d)", - hdev->name, err); + bt_dev_err(hdev, "BCM: failed to write clock (%d)", + err); return err; } kfree_skb(skb); } - BT_DBG("%s: Set Controller UART speed to %d bit/s", hdev->name, speed); + bt_dev_dbg(hdev, "Set Controller UART speed to %d bit/s", speed); param.zero = cpu_to_le16(0); param.baud_rate = cpu_to_le32(speed); @@ -108,8 +118,8 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed) HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); - BT_ERR("%s: BCM: failed to write update baudrate command (%d)", - hdev->name, err); + bt_dev_err(hdev, "BCM: failed to write update baudrate (%d)", + err); return err; } @@ -149,12 +159,125 @@ static int bcm_gpio_set_power(struct bcm_device *dev, bool powered) return 0; } +#ifdef CONFIG_PM +static irqreturn_t bcm_host_wake(int irq, void *data) +{ + struct bcm_device *bdev = data; + + bt_dev_dbg(bdev, "Host wake IRQ"); + + pm_runtime_get(&bdev->pdev->dev); + pm_runtime_mark_last_busy(&bdev->pdev->dev); + pm_runtime_put_autosuspend(&bdev->pdev->dev); + + return IRQ_HANDLED; +} + +static int bcm_request_irq(struct bcm_data *bcm) +{ + struct bcm_device *bdev = bcm->dev; + int err = 0; + + /* If this is not a platform device, do not enable PM functionalities */ + mutex_lock(&bcm_device_lock); + if (!bcm_device_exists(bdev)) { + err = -ENODEV; + goto unlock; + } + + if (bdev->irq > 0) { + err = devm_request_irq(&bdev->pdev->dev, bdev->irq, + bcm_host_wake, IRQF_TRIGGER_RISING, + "host_wake", bdev); + if (err) + goto unlock; + + device_init_wakeup(&bdev->pdev->dev, true); + + pm_runtime_set_autosuspend_delay(&bdev->pdev->dev, + BCM_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&bdev->pdev->dev); + pm_runtime_set_active(&bdev->pdev->dev); + pm_runtime_enable(&bdev->pdev->dev); + } + +unlock: + mutex_unlock(&bcm_device_lock); + + return err; +} + +static const struct bcm_set_sleep_mode default_sleep_params = { + .sleep_mode = 1, /* 0=Disabled, 1=UART, 2=Reserved, 3=USB */ + .idle_host = 2, /* idle threshold HOST, in 300ms */ + .idle_dev = 2, /* idle threshold device, in 300ms */ + .bt_wake_active = 1, /* BT_WAKE active mode: 1 = high, 0 = low */ + .host_wake_active = 0, /* HOST_WAKE active mode: 1 = high, 0 = low */ + .allow_host_sleep = 1, /* Allow host sleep in SCO flag */ + .combine_modes = 1, /* Combine sleep and LPM flag */ + .tristate_control = 0, /* Allow tri-state control of UART tx flag */ + /* Irrelevant USB flags */ + .usb_auto_sleep = 0, + .usb_resume_timeout = 0, + .pulsed_host_wake = 0, + .break_to_host = 0 +}; + +static int bcm_setup_sleep(struct hci_uart *hu) +{ + struct bcm_data *bcm = hu->priv; + struct sk_buff *skb; + struct bcm_set_sleep_mode sleep_params = default_sleep_params; + + sleep_params.host_wake_active = !bcm->dev->irq_polarity; + + skb = __hci_cmd_sync(hu->hdev, 0xfc27, sizeof(sleep_params), + &sleep_params, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + int err = PTR_ERR(skb); + bt_dev_err(hu->hdev, "Sleep VSC failed (%d)", err); + return err; + } + kfree_skb(skb); + + bt_dev_dbg(hu->hdev, "Set Sleep Parameters VSC succeeded"); + + return 0; +} +#else +static inline int bcm_request_irq(struct bcm_data *bcm) { return 0; } +static inline int bcm_setup_sleep(struct hci_uart *hu) { return 0; } +#endif + +static int bcm_set_diag(struct hci_dev *hdev, bool enable) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct bcm_data *bcm = hu->priv; + struct sk_buff *skb; + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -ENETDOWN; + + skb = bt_skb_alloc(3, GFP_KERNEL); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + *skb_put(skb, 1) = BCM_LM_DIAG_PKT; + *skb_put(skb, 1) = 0xf0; + *skb_put(skb, 1) = enable; + + skb_queue_tail(&bcm->txq, skb); + hci_uart_tx_wakeup(hu); + + return 0; +} + static int bcm_open(struct hci_uart *hu) { struct bcm_data *bcm; struct list_head *p; - BT_DBG("hu %p", hu); + bt_dev_dbg(hu->hdev, "hu %p", hu); bcm = kzalloc(sizeof(*bcm), GFP_KERNEL); if (!bcm) @@ -164,7 +287,7 @@ static int bcm_open(struct hci_uart *hu) hu->priv = bcm; - spin_lock(&bcm_device_lock); + mutex_lock(&bcm_device_lock); list_for_each(p, &bcm_device_list) { struct bcm_device *dev = list_entry(p, struct bcm_device, list); @@ -175,17 +298,15 @@ static int bcm_open(struct hci_uart *hu) if (hu->tty->dev->parent == dev->pdev->dev.parent) { bcm->dev = dev; hu->init_speed = dev->init_speed; -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM dev->hu = hu; #endif + bcm_gpio_set_power(bcm->dev, true); break; } } - if (bcm->dev) - bcm_gpio_set_power(bcm->dev, true); - - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); return 0; } @@ -193,18 +314,27 @@ static int bcm_open(struct hci_uart *hu) static int bcm_close(struct hci_uart *hu) { struct bcm_data *bcm = hu->priv; + struct bcm_device *bdev = bcm->dev; - BT_DBG("hu %p", hu); + bt_dev_dbg(hu->hdev, "hu %p", hu); /* Protect bcm->dev against removal of the device or driver */ - spin_lock(&bcm_device_lock); - if (bcm_device_exists(bcm->dev)) { - bcm_gpio_set_power(bcm->dev, false); -#ifdef CONFIG_PM_SLEEP - bcm->dev->hu = NULL; + mutex_lock(&bcm_device_lock); + if (bcm_device_exists(bdev)) { + bcm_gpio_set_power(bdev, false); +#ifdef CONFIG_PM + pm_runtime_disable(&bdev->pdev->dev); + pm_runtime_set_suspended(&bdev->pdev->dev); + + if (device_can_wakeup(&bdev->pdev->dev)) { + devm_free_irq(&bdev->pdev->dev, bdev->irq, bdev); + device_init_wakeup(&bdev->pdev->dev, false); + } + + bdev->hu = NULL; #endif } - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); skb_queue_purge(&bcm->txq); kfree_skb(bcm->rx_skb); @@ -218,7 +348,7 @@ static int bcm_flush(struct hci_uart *hu) { struct bcm_data *bcm = hu->priv; - BT_DBG("hu %p", hu); + bt_dev_dbg(hu->hdev, "hu %p", hu); skb_queue_purge(&bcm->txq); @@ -227,13 +357,15 @@ static int bcm_flush(struct hci_uart *hu) static int bcm_setup(struct hci_uart *hu) { + struct bcm_data *bcm = hu->priv; char fw_name[64]; const struct firmware *fw; unsigned int speed; int err; - BT_DBG("hu %p", hu); + bt_dev_dbg(hu->hdev, "hu %p", hu); + hu->hdev->set_diag = bcm_set_diag; hu->hdev->set_bdaddr = btbcm_set_bdaddr; err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name)); @@ -242,13 +374,13 @@ static int bcm_setup(struct hci_uart *hu) err = request_firmware(&fw, fw_name, &hu->hdev->dev); if (err < 0) { - BT_INFO("%s: BCM: Patch %s not found", hu->hdev->name, fw_name); + bt_dev_info(hu->hdev, "BCM: Patch %s not found", fw_name); return 0; } err = btbcm_patchram(hu->hdev, fw); if (err) { - BT_INFO("%s: BCM: Patch failed (%d)", hu->hdev->name, err); + bt_dev_info(hu->hdev, "BCM: Patch failed (%d)", err); goto finalize; } @@ -281,14 +413,28 @@ finalize: release_firmware(fw); err = btbcm_finalize(hu->hdev); + if (err) + return err; + + err = bcm_request_irq(bcm); + if (!err) + err = bcm_setup_sleep(hu); return err; } +#define BCM_RECV_LM_DIAG \ + .type = BCM_LM_DIAG_PKT, \ + .hlen = BCM_LM_DIAG_SIZE, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = BCM_LM_DIAG_SIZE + static const struct h4_recv_pkt bcm_recv_pkts[] = { - { H4_RECV_ACL, .recv = hci_recv_frame }, - { H4_RECV_SCO, .recv = hci_recv_frame }, - { H4_RECV_EVENT, .recv = hci_recv_frame }, + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, + { BCM_RECV_LM_DIAG, .recv = hci_recv_diag }, }; static int bcm_recv(struct hci_uart *hu, const void *data, int count) @@ -302,9 +448,18 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count) bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts)); if (IS_ERR(bcm->rx_skb)) { int err = PTR_ERR(bcm->rx_skb); - BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err); bcm->rx_skb = NULL; return err; + } else if (!bcm->rx_skb) { + /* Delay auto-suspend when receiving completed packet */ + mutex_lock(&bcm_device_lock); + if (bcm->dev && bcm_device_exists(bcm->dev)) { + pm_runtime_get(&bcm->dev->pdev->dev); + pm_runtime_mark_last_busy(&bcm->dev->pdev->dev); + pm_runtime_put_autosuspend(&bcm->dev->pdev->dev); + } + mutex_unlock(&bcm_device_lock); } return count; @@ -314,7 +469,7 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct bcm_data *bcm = hu->priv; - BT_DBG("hu %p skb %p", hu, skb); + bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb); /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); @@ -326,39 +481,105 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb) static struct sk_buff *bcm_dequeue(struct hci_uart *hu) { struct bcm_data *bcm = hu->priv; + struct sk_buff *skb = NULL; + struct bcm_device *bdev = NULL; + + mutex_lock(&bcm_device_lock); + + if (bcm_device_exists(bcm->dev)) { + bdev = bcm->dev; + pm_runtime_get_sync(&bdev->pdev->dev); + /* Shall be resumed here */ + } + + skb = skb_dequeue(&bcm->txq); + + if (bdev) { + pm_runtime_mark_last_busy(&bdev->pdev->dev); + pm_runtime_put_autosuspend(&bdev->pdev->dev); + } - return skb_dequeue(&bcm->txq); + mutex_unlock(&bcm_device_lock); + + return skb; } -#ifdef CONFIG_PM_SLEEP -/* Platform suspend callback */ -static int bcm_suspend(struct device *dev) +#ifdef CONFIG_PM +static int bcm_suspend_device(struct device *dev) { struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev)); - BT_DBG("suspend (%p): is_suspended %d", bdev, bdev->is_suspended); - - spin_lock(&bcm_device_lock); + bt_dev_dbg(bdev, ""); - if (!bdev->hu) - goto unlock; - - if (!bdev->is_suspended) { + if (!bdev->is_suspended && bdev->hu) { hci_uart_set_flow_control(bdev->hu, true); - /* Once this callback returns, driver suspends BT via GPIO */ + /* Once this returns, driver suspends BT via GPIO */ bdev->is_suspended = true; } /* Suspend the device */ if (bdev->device_wakeup) { gpiod_set_value(bdev->device_wakeup, false); - BT_DBG("suspend, delaying 15 ms"); + bt_dev_dbg(bdev, "suspend, delaying 15 ms"); + mdelay(15); + } + + return 0; +} + +static int bcm_resume_device(struct device *dev) +{ + struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev)); + + bt_dev_dbg(bdev, ""); + + if (bdev->device_wakeup) { + gpiod_set_value(bdev->device_wakeup, true); + bt_dev_dbg(bdev, "resume, delaying 15 ms"); mdelay(15); } + /* When this executes, the device has woken up already */ + if (bdev->is_suspended && bdev->hu) { + bdev->is_suspended = false; + + hci_uart_set_flow_control(bdev->hu, false); + } + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +/* Platform suspend callback */ +static int bcm_suspend(struct device *dev) +{ + struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev)); + int error; + + bt_dev_dbg(bdev, "suspend: is_suspended %d", bdev->is_suspended); + + /* bcm_suspend can be called at any time as long as platform device is + * bound, so it should use bcm_device_lock to protect access to hci_uart + * and device_wake-up GPIO. + */ + mutex_lock(&bcm_device_lock); + + if (!bdev->hu) + goto unlock; + + if (pm_runtime_active(dev)) + bcm_suspend_device(dev); + + if (device_may_wakeup(&bdev->pdev->dev)) { + error = enable_irq_wake(bdev->irq); + if (!error) + bt_dev_dbg(bdev, "BCM irq: enabled"); + } + unlock: - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); return 0; } @@ -368,28 +589,30 @@ static int bcm_resume(struct device *dev) { struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev)); - BT_DBG("resume (%p): is_suspended %d", bdev, bdev->is_suspended); + bt_dev_dbg(bdev, "resume: is_suspended %d", bdev->is_suspended); - spin_lock(&bcm_device_lock); + /* bcm_resume can be called at any time as long as platform device is + * bound, so it should use bcm_device_lock to protect access to hci_uart + * and device_wake-up GPIO. + */ + mutex_lock(&bcm_device_lock); if (!bdev->hu) goto unlock; - if (bdev->device_wakeup) { - gpiod_set_value(bdev->device_wakeup, true); - BT_DBG("resume, delaying 15 ms"); - mdelay(15); + if (device_may_wakeup(&bdev->pdev->dev)) { + disable_irq_wake(bdev->irq); + bt_dev_dbg(bdev, "BCM irq: disabled"); } - /* When this callback executes, the device has woken up already */ - if (bdev->is_suspended) { - bdev->is_suspended = false; - - hci_uart_set_flow_control(bdev->hu, false); - } + bcm_resume_device(dev); unlock: - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); + + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); return 0; } @@ -397,24 +620,59 @@ unlock: static const struct acpi_gpio_params device_wakeup_gpios = { 0, 0, false }; static const struct acpi_gpio_params shutdown_gpios = { 1, 0, false }; +static const struct acpi_gpio_params host_wakeup_gpios = { 2, 0, false }; static const struct acpi_gpio_mapping acpi_bcm_default_gpios[] = { { "device-wakeup-gpios", &device_wakeup_gpios, 1 }, { "shutdown-gpios", &shutdown_gpios, 1 }, + { "host-wakeup-gpios", &host_wakeup_gpios, 1 }, { }, }; #ifdef CONFIG_ACPI +static u8 acpi_active_low = ACPI_ACTIVE_LOW; + +/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */ +static const struct dmi_system_id bcm_wrong_irq_dmi_table[] = { + { + .ident = "Asus T100TA", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, + "ASUSTeK COMPUTER INC."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), + }, + .driver_data = &acpi_active_low, + }, + { } +}; + static int bcm_resource(struct acpi_resource *ares, void *data) { struct bcm_device *dev = data; - - if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { - struct acpi_resource_uart_serialbus *sb; - + struct acpi_resource_extended_irq *irq; + struct acpi_resource_gpio *gpio; + struct acpi_resource_uart_serialbus *sb; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + irq = &ares->data.extended_irq; + dev->irq_polarity = irq->polarity; + break; + + case ACPI_RESOURCE_TYPE_GPIO: + gpio = &ares->data.gpio; + if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) + dev->irq_polarity = gpio->polarity; + break; + + case ACPI_RESOURCE_TYPE_SERIAL_BUS: sb = &ares->data.uart_serial_bus; if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_UART) dev->init_speed = sb->default_baud_rate; + break; + + default: + break; } /* Always tell the ACPI core to skip this resource */ @@ -424,15 +682,10 @@ static int bcm_resource(struct acpi_resource *ares, void *data) static int bcm_acpi_probe(struct bcm_device *dev) { struct platform_device *pdev = dev->pdev; - const struct acpi_device_id *id; - struct acpi_device *adev; LIST_HEAD(resources); + const struct dmi_system_id *dmi_id; int ret; - id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); - if (!id) - return -ENODEV; - /* Retrieve GPIO data */ dev->name = dev_name(&pdev->dev); ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev), @@ -453,6 +706,21 @@ static int bcm_acpi_probe(struct bcm_device *dev) if (IS_ERR(dev->shutdown)) return PTR_ERR(dev->shutdown); + /* IRQ can be declared in ACPI table as Interrupt or GpioInt */ + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq <= 0) { + struct gpio_desc *gpio; + + gpio = devm_gpiod_get_optional(&pdev->dev, "host-wakeup", + GPIOD_IN); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + dev->irq = gpiod_to_irq(gpio); + } + + dev_info(&pdev->dev, "BCM irq: %d\n", dev->irq); + /* Make sure at-least one of the GPIO is defined and that * a name is specified for this instance */ @@ -462,11 +730,18 @@ static int bcm_acpi_probe(struct bcm_device *dev) } /* Retrieve UART ACPI info */ - adev = ACPI_COMPANION(&dev->pdev->dev); - if (!adev) - return 0; + ret = acpi_dev_get_resources(ACPI_COMPANION(&dev->pdev->dev), + &resources, bcm_resource, dev); + if (ret < 0) + return ret; + acpi_dev_free_resource_list(&resources); - acpi_dev_get_resources(adev, &resources, bcm_resource, dev); + dmi_id = dmi_first_match(bcm_wrong_irq_dmi_table); + if (dmi_id) { + bt_dev_warn(dev, "%s: Overwriting IRQ polarity to active low", + dmi_id->ident); + dev->irq_polarity = *(u8 *)dmi_id->driver_data; + } return 0; } @@ -480,7 +755,6 @@ static int bcm_acpi_probe(struct bcm_device *dev) static int bcm_probe(struct platform_device *pdev) { struct bcm_device *dev; - struct acpi_device_id *pdata = pdev->dev.platform_data; int ret; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -489,24 +763,18 @@ static int bcm_probe(struct platform_device *pdev) dev->pdev = pdev; - if (ACPI_HANDLE(&pdev->dev)) { - ret = bcm_acpi_probe(dev); - if (ret) - return ret; - } else if (pdata) { - dev->name = pdata->id; - } else { - return -ENODEV; - } + ret = bcm_acpi_probe(dev); + if (ret) + return ret; platform_set_drvdata(pdev, dev); dev_info(&pdev->dev, "%s device registered.\n", dev->name); /* Place this instance on the device list */ - spin_lock(&bcm_device_lock); + mutex_lock(&bcm_device_lock); list_add_tail(&dev->list, &bcm_device_list); - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); bcm_gpio_set_power(dev, false); @@ -517,9 +785,9 @@ static int bcm_remove(struct platform_device *pdev) { struct bcm_device *dev = platform_get_drvdata(pdev); - spin_lock(&bcm_device_lock); + mutex_lock(&bcm_device_lock); list_del(&dev->list); - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pdev->dev)); @@ -553,7 +821,10 @@ MODULE_DEVICE_TABLE(acpi, bcm_acpi_match); #endif /* Platform suspend and resume callbacks */ -static SIMPLE_DEV_PM_OPS(bcm_pm_ops, bcm_suspend, bcm_resume); +static const struct dev_pm_ops bcm_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(bcm_suspend, bcm_resume) + SET_RUNTIME_PM_OPS(bcm_suspend_device, bcm_resume_device, NULL) +}; static struct platform_driver bcm_driver = { .probe = bcm_probe, diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index eec3f28e4bb9..a6fce48da0fb 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -266,3 +266,4 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, return skb; } +EXPORT_SYMBOL_GPL(h4_recv_buf); diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index b35b238a0380..abee2216fdeb 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -128,7 +128,7 @@ static void h5_timed_event(unsigned long arg) { const unsigned char sync_req[] = { 0x01, 0x7e }; unsigned char conf_req[] = { 0x03, 0xfc, 0x01 }; - struct hci_uart *hu = (struct hci_uart *) arg; + struct hci_uart *hu = (struct hci_uart *)arg; struct h5 *h5 = hu->priv; struct sk_buff *skb; unsigned long flags; @@ -210,7 +210,7 @@ static int h5_open(struct hci_uart *hu) init_timer(&h5->timer); h5->timer.function = h5_timed_event; - h5->timer.data = (unsigned long) hu; + h5->timer.data = (unsigned long)hu; h5->tx_win = H5_TX_WIN_MAX; @@ -453,7 +453,7 @@ static int h5_rx_pkt_start(struct hci_uart *hu, unsigned char c) return -ENOMEM; } - h5->rx_skb->dev = (void *) hu->hdev; + h5->rx_skb->dev = (void *)hu->hdev; return 0; } @@ -696,7 +696,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu) } skb = skb_dequeue(&h5->unrel); - if (skb != NULL) { + if (skb) { nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type, skb->data, skb->len); if (nskb) { @@ -714,7 +714,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu) goto unlock; skb = skb_dequeue(&h5->rel); - if (skb != NULL) { + if (skb) { nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type, skb->data, skb->len); if (nskb) { diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index cf07d1121956..2952107e3bae 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -31,6 +31,8 @@ #include <linux/platform_device.h> #include <linux/gpio/consumer.h> #include <linux/acpi.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -43,19 +45,45 @@ #define STATE_FIRMWARE_LOADED 2 #define STATE_FIRMWARE_FAILED 3 #define STATE_BOOTING 4 +#define STATE_LPM_ENABLED 5 +#define STATE_TX_ACTIVE 6 +#define STATE_SUSPENDED 7 +#define STATE_LPM_TRANSACTION 8 + +#define HCI_LPM_WAKE_PKT 0xf0 +#define HCI_LPM_PKT 0xf1 +#define HCI_LPM_MAX_SIZE 10 +#define HCI_LPM_HDR_SIZE HCI_EVENT_HDR_SIZE + +#define LPM_OP_TX_NOTIFY 0x00 +#define LPM_OP_SUSPEND_ACK 0x02 +#define LPM_OP_RESUME_ACK 0x03 + +#define LPM_SUSPEND_DELAY_MS 1000 + +struct hci_lpm_pkt { + __u8 opcode; + __u8 dlen; + __u8 data[0]; +} __packed; struct intel_device { struct list_head list; struct platform_device *pdev; struct gpio_desc *reset; + struct hci_uart *hu; + struct mutex hu_lock; + int irq; }; static LIST_HEAD(intel_device_list); -static DEFINE_SPINLOCK(intel_device_list_lock); +static DEFINE_MUTEX(intel_device_list_lock); struct intel_data { struct sk_buff *rx_skb; struct sk_buff_head txq; + struct work_struct busy_work; + struct hci_uart *hu; unsigned long flags; }; @@ -101,24 +129,185 @@ static int intel_wait_booting(struct hci_uart *hu) msecs_to_jiffies(1000)); if (err == 1) { - BT_ERR("%s: Device boot interrupted", hu->hdev->name); + bt_dev_err(hu->hdev, "Device boot interrupted"); return -EINTR; } if (err) { - BT_ERR("%s: Device boot timeout", hu->hdev->name); + bt_dev_err(hu->hdev, "Device boot timeout"); return -ETIMEDOUT; } return err; } +#ifdef CONFIG_PM +static int intel_wait_lpm_transaction(struct hci_uart *hu) +{ + struct intel_data *intel = hu->priv; + int err; + + err = wait_on_bit_timeout(&intel->flags, STATE_LPM_TRANSACTION, + TASK_INTERRUPTIBLE, + msecs_to_jiffies(1000)); + + if (err == 1) { + bt_dev_err(hu->hdev, "LPM transaction interrupted"); + return -EINTR; + } + + if (err) { + bt_dev_err(hu->hdev, "LPM transaction timeout"); + return -ETIMEDOUT; + } + + return err; +} + +static int intel_lpm_suspend(struct hci_uart *hu) +{ + static const u8 suspend[] = { 0x01, 0x01, 0x01 }; + struct intel_data *intel = hu->priv; + struct sk_buff *skb; + + if (!test_bit(STATE_LPM_ENABLED, &intel->flags) || + test_bit(STATE_SUSPENDED, &intel->flags)) + return 0; + + if (test_bit(STATE_TX_ACTIVE, &intel->flags)) + return -EAGAIN; + + bt_dev_dbg(hu->hdev, "Suspending"); + + skb = bt_skb_alloc(sizeof(suspend), GFP_KERNEL); + if (!skb) { + bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet"); + return -ENOMEM; + } + + memcpy(skb_put(skb, sizeof(suspend)), suspend, sizeof(suspend)); + bt_cb(skb)->pkt_type = HCI_LPM_PKT; + + set_bit(STATE_LPM_TRANSACTION, &intel->flags); + + /* LPM flow is a priority, enqueue packet at list head */ + skb_queue_head(&intel->txq, skb); + hci_uart_tx_wakeup(hu); + + intel_wait_lpm_transaction(hu); + /* Even in case of failure, continue and test the suspended flag */ + + clear_bit(STATE_LPM_TRANSACTION, &intel->flags); + + if (!test_bit(STATE_SUSPENDED, &intel->flags)) { + bt_dev_err(hu->hdev, "Device suspend error"); + return -EINVAL; + } + + bt_dev_dbg(hu->hdev, "Suspended"); + + hci_uart_set_flow_control(hu, true); + + return 0; +} + +static int intel_lpm_resume(struct hci_uart *hu) +{ + struct intel_data *intel = hu->priv; + struct sk_buff *skb; + + if (!test_bit(STATE_LPM_ENABLED, &intel->flags) || + !test_bit(STATE_SUSPENDED, &intel->flags)) + return 0; + + bt_dev_dbg(hu->hdev, "Resuming"); + + hci_uart_set_flow_control(hu, false); + + skb = bt_skb_alloc(0, GFP_KERNEL); + if (!skb) { + bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet"); + return -ENOMEM; + } + + bt_cb(skb)->pkt_type = HCI_LPM_WAKE_PKT; + + set_bit(STATE_LPM_TRANSACTION, &intel->flags); + + /* LPM flow is a priority, enqueue packet at list head */ + skb_queue_head(&intel->txq, skb); + hci_uart_tx_wakeup(hu); + + intel_wait_lpm_transaction(hu); + /* Even in case of failure, continue and test the suspended flag */ + + clear_bit(STATE_LPM_TRANSACTION, &intel->flags); + + if (test_bit(STATE_SUSPENDED, &intel->flags)) { + bt_dev_err(hu->hdev, "Device resume error"); + return -EINVAL; + } + + bt_dev_dbg(hu->hdev, "Resumed"); + + return 0; +} +#endif /* CONFIG_PM */ + +static int intel_lpm_host_wake(struct hci_uart *hu) +{ + static const u8 lpm_resume_ack[] = { LPM_OP_RESUME_ACK, 0x00 }; + struct intel_data *intel = hu->priv; + struct sk_buff *skb; + + hci_uart_set_flow_control(hu, false); + + clear_bit(STATE_SUSPENDED, &intel->flags); + + skb = bt_skb_alloc(sizeof(lpm_resume_ack), GFP_KERNEL); + if (!skb) { + bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet"); + return -ENOMEM; + } + + memcpy(skb_put(skb, sizeof(lpm_resume_ack)), lpm_resume_ack, + sizeof(lpm_resume_ack)); + bt_cb(skb)->pkt_type = HCI_LPM_PKT; + + /* LPM flow is a priority, enqueue packet at list head */ + skb_queue_head(&intel->txq, skb); + hci_uart_tx_wakeup(hu); + + bt_dev_dbg(hu->hdev, "Resumed by controller"); + + return 0; +} + +static irqreturn_t intel_irq(int irq, void *dev_id) +{ + struct intel_device *idev = dev_id; + + dev_info(&idev->pdev->dev, "hci_intel irq\n"); + + mutex_lock(&idev->hu_lock); + if (idev->hu) + intel_lpm_host_wake(idev->hu); + mutex_unlock(&idev->hu_lock); + + /* Host/Controller are now LPM resumed, trigger a new delayed suspend */ + pm_runtime_get(&idev->pdev->dev); + pm_runtime_mark_last_busy(&idev->pdev->dev); + pm_runtime_put_autosuspend(&idev->pdev->dev); + + return IRQ_HANDLED; +} + static int intel_set_power(struct hci_uart *hu, bool powered) { struct list_head *p; int err = -ENODEV; - spin_lock(&intel_device_list_lock); + mutex_lock(&intel_device_list_lock); list_for_each(p, &intel_device_list) { struct intel_device *idev = list_entry(p, struct intel_device, @@ -139,13 +328,73 @@ static int intel_set_power(struct hci_uart *hu, bool powered) hu, dev_name(&idev->pdev->dev), powered); gpiod_set_value(idev->reset, powered); + + /* Provide to idev a hu reference which is used to run LPM + * transactions (lpm suspend/resume) from PM callbacks. + * hu needs to be protected against concurrent removing during + * these PM ops. + */ + mutex_lock(&idev->hu_lock); + idev->hu = powered ? hu : NULL; + mutex_unlock(&idev->hu_lock); + + if (idev->irq < 0) + break; + + if (powered && device_can_wakeup(&idev->pdev->dev)) { + err = devm_request_threaded_irq(&idev->pdev->dev, + idev->irq, NULL, + intel_irq, + IRQF_ONESHOT, + "bt-host-wake", idev); + if (err) { + BT_ERR("hu %p, unable to allocate irq-%d", + hu, idev->irq); + break; + } + + device_wakeup_enable(&idev->pdev->dev); + + pm_runtime_set_active(&idev->pdev->dev); + pm_runtime_use_autosuspend(&idev->pdev->dev); + pm_runtime_set_autosuspend_delay(&idev->pdev->dev, + LPM_SUSPEND_DELAY_MS); + pm_runtime_enable(&idev->pdev->dev); + } else if (!powered && device_may_wakeup(&idev->pdev->dev)) { + devm_free_irq(&idev->pdev->dev, idev->irq, idev); + device_wakeup_disable(&idev->pdev->dev); + + pm_runtime_disable(&idev->pdev->dev); + } } - spin_unlock(&intel_device_list_lock); + mutex_unlock(&intel_device_list_lock); return err; } +static void intel_busy_work(struct work_struct *work) +{ + struct list_head *p; + struct intel_data *intel = container_of(work, struct intel_data, + busy_work); + + /* Link is busy, delay the suspend */ + mutex_lock(&intel_device_list_lock); + list_for_each(p, &intel_device_list) { + struct intel_device *idev = list_entry(p, struct intel_device, + list); + + if (intel->hu->tty->dev->parent == idev->pdev->dev.parent) { + pm_runtime_get(&idev->pdev->dev); + pm_runtime_mark_last_busy(&idev->pdev->dev); + pm_runtime_put_autosuspend(&idev->pdev->dev); + break; + } + } + mutex_unlock(&intel_device_list_lock); +} + static int intel_open(struct hci_uart *hu) { struct intel_data *intel; @@ -157,6 +406,9 @@ static int intel_open(struct hci_uart *hu) return -ENOMEM; skb_queue_head_init(&intel->txq); + INIT_WORK(&intel->busy_work, intel_busy_work); + + intel->hu = hu; hu->priv = intel; @@ -172,6 +424,8 @@ static int intel_close(struct hci_uart *hu) BT_DBG("hu %p", hu); + cancel_work_sync(&intel->busy_work); + intel_set_power(hu, false); skb_queue_purge(&intel->txq); @@ -237,11 +491,11 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed) if (err && err != ETIMEDOUT) return err; - BT_INFO("%s: Change controller speed to %d", hdev->name, speed); + bt_dev_info(hdev, "Change controller speed to %d", speed); speed_cmd[3] = intel_convert_speed(speed); if (speed_cmd[3] == 0xff) { - BT_ERR("%s: Unsupported speed", hdev->name); + bt_dev_err(hdev, "Unsupported speed"); return -EINVAL; } @@ -250,16 +504,15 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed) */ skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: Reading Intel version information failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "Reading Intel version information failed (%ld)", + PTR_ERR(skb)); return PTR_ERR(skb); } kfree_skb(skb); skb = bt_skb_alloc(sizeof(speed_cmd), GFP_KERNEL); if (!skb) { - BT_ERR("%s: Failed to allocate memory for baudrate packet", - hdev->name); + bt_dev_err(hdev, "Failed to alloc memory for baudrate packet"); return -ENOMEM; } @@ -284,11 +537,14 @@ static int intel_setup(struct hci_uart *hu) { static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x04, 0x00 }; + static const u8 lpm_param[] = { 0x03, 0x07, 0x01, 0x0b }; struct intel_data *intel = hu->priv; + struct intel_device *idev = NULL; struct hci_dev *hdev = hu->hdev; struct sk_buff *skb; struct intel_version *ver; struct intel_boot_params *params; + struct list_head *p; const struct firmware *fw; const u8 *fw_ptr; char fwname[64]; @@ -299,7 +555,7 @@ static int intel_setup(struct hci_uart *hu) int speed_change = 0; int err; - BT_DBG("%s", hdev->name); + bt_dev_dbg(hdev, "start intel_setup"); hu->hdev->set_bdaddr = btintel_set_bdaddr; @@ -335,21 +591,21 @@ static int intel_setup(struct hci_uart *hu) */ skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: Reading Intel version information failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "Reading Intel version information failed (%ld)", + PTR_ERR(skb)); return PTR_ERR(skb); } if (skb->len != sizeof(*ver)) { - BT_ERR("%s: Intel version event size mismatch", hdev->name); + bt_dev_err(hdev, "Intel version event size mismatch"); kfree_skb(skb); return -EILSEQ; } ver = (struct intel_version *)skb->data; if (ver->status) { - BT_ERR("%s: Intel version command failure (%02x)", - hdev->name, ver->status); + bt_dev_err(hdev, "Intel version command failure (%02x)", + ver->status); err = -bt_to_errno(ver->status); kfree_skb(skb); return err; @@ -359,8 +615,8 @@ static int intel_setup(struct hci_uart *hu) * for now only accept this single value. */ if (ver->hw_platform != 0x37) { - BT_ERR("%s: Unsupported Intel hardware platform (%u)", - hdev->name, ver->hw_platform); + bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)", + ver->hw_platform); kfree_skb(skb); return -EINVAL; } @@ -371,8 +627,8 @@ static int intel_setup(struct hci_uart *hu) * when newer hardware variants come along. */ if (ver->hw_variant != 0x0b) { - BT_ERR("%s: Unsupported Intel hardware variant (%u)", - hdev->name, ver->hw_variant); + bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)", + ver->hw_variant); kfree_skb(skb); return -EINVAL; } @@ -403,8 +659,8 @@ static int intel_setup(struct hci_uart *hu) * choice is to return an error and abort the device initialization. */ if (ver->fw_variant != 0x06) { - BT_ERR("%s: Unsupported Intel firmware variant (%u)", - hdev->name, ver->fw_variant); + bt_dev_err(hdev, "Unsupported Intel firmware variant (%u)", + ver->fw_variant); kfree_skb(skb); return -ENODEV; } @@ -416,33 +672,33 @@ static int intel_setup(struct hci_uart *hu) */ skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: Reading Intel boot parameters failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "Reading Intel boot parameters failed (%ld)", + PTR_ERR(skb)); return PTR_ERR(skb); } if (skb->len != sizeof(*params)) { - BT_ERR("%s: Intel boot parameters size mismatch", hdev->name); + bt_dev_err(hdev, "Intel boot parameters size mismatch"); kfree_skb(skb); return -EILSEQ; } params = (struct intel_boot_params *)skb->data; if (params->status) { - BT_ERR("%s: Intel boot parameters command failure (%02x)", - hdev->name, params->status); + bt_dev_err(hdev, "Intel boot parameters command failure (%02x)", + params->status); err = -bt_to_errno(params->status); kfree_skb(skb); return err; } - BT_INFO("%s: Device revision is %u", hdev->name, - le16_to_cpu(params->dev_revid)); + bt_dev_info(hdev, "Device revision is %u", + le16_to_cpu(params->dev_revid)); - BT_INFO("%s: Secure boot is %s", hdev->name, - params->secure_boot ? "enabled" : "disabled"); + bt_dev_info(hdev, "Secure boot is %s", + params->secure_boot ? "enabled" : "disabled"); - BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name, + bt_dev_info(hdev, "Minimum firmware build %u week %u %u", params->min_fw_build_nn, params->min_fw_build_cw, 2000 + params->min_fw_build_yy); @@ -451,8 +707,8 @@ static int intel_setup(struct hci_uart *hu) * that this bootloader does not send them, then abort the setup. */ if (params->limited_cce != 0x00) { - BT_ERR("%s: Unsupported Intel firmware loading method (%u)", - hdev->name, params->limited_cce); + bt_dev_err(hdev, "Unsupported Intel firmware loading method (%u)", + params->limited_cce); kfree_skb(skb); return -EINVAL; } @@ -461,7 +717,7 @@ static int intel_setup(struct hci_uart *hu) * also be no valid address for the operational firmware. */ if (!bacmp(¶ms->otp_bdaddr, BDADDR_ANY)) { - BT_INFO("%s: No device address configured", hdev->name); + bt_dev_info(hdev, "No device address configured"); set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); } @@ -476,19 +732,23 @@ static int intel_setup(struct hci_uart *hu) err = request_firmware(&fw, fwname, &hdev->dev); if (err < 0) { - BT_ERR("%s: Failed to load Intel firmware file (%d)", - hdev->name, err); + bt_dev_err(hdev, "Failed to load Intel firmware file (%d)", + err); kfree_skb(skb); return err; } - BT_INFO("%s: Found device firmware: %s", hdev->name, fwname); + bt_dev_info(hdev, "Found device firmware: %s", fwname); + + /* Save the DDC file name for later */ + snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.ddc", + le16_to_cpu(params->dev_revid)); kfree_skb(skb); if (fw->size < 644) { - BT_ERR("%s: Invalid size of firmware file (%zu)", - hdev->name, fw->size); + bt_dev_err(hdev, "Invalid size of firmware file (%zu)", + fw->size); err = -EBADF; goto done; } @@ -500,8 +760,7 @@ static int intel_setup(struct hci_uart *hu) */ err = btintel_secure_send(hdev, 0x00, 128, fw->data); if (err < 0) { - BT_ERR("%s: Failed to send firmware header (%d)", - hdev->name, err); + bt_dev_err(hdev, "Failed to send firmware header (%d)", err); goto done; } @@ -510,8 +769,8 @@ static int intel_setup(struct hci_uart *hu) */ err = btintel_secure_send(hdev, 0x03, 256, fw->data + 128); if (err < 0) { - BT_ERR("%s: Failed to send firmware public key (%d)", - hdev->name, err); + bt_dev_err(hdev, "Failed to send firmware public key (%d)", + err); goto done; } @@ -520,8 +779,8 @@ static int intel_setup(struct hci_uart *hu) */ err = btintel_secure_send(hdev, 0x02, 256, fw->data + 388); if (err < 0) { - BT_ERR("%s: Failed to send firmware signature (%d)", - hdev->name, err); + bt_dev_err(hdev, "Failed to send firmware signature (%d)", + err); goto done; } @@ -533,8 +792,8 @@ static int intel_setup(struct hci_uart *hu) frag_len += sizeof(*cmd) + cmd->plen; - BT_DBG("%s: patching %td/%zu", hdev->name, - (fw_ptr - fw->data), fw->size); + bt_dev_dbg(hdev, "Patching %td/%zu", (fw_ptr - fw->data), + fw->size); /* The parameter length of the secure send command requires * a 4 byte alignment. It happens so that the firmware file @@ -552,8 +811,8 @@ static int intel_setup(struct hci_uart *hu) */ err = btintel_secure_send(hdev, 0x01, frag_len, fw_ptr); if (err < 0) { - BT_ERR("%s: Failed to send firmware data (%d)", - hdev->name, err); + bt_dev_err(hdev, "Failed to send firmware data (%d)", + err); goto done; } @@ -563,7 +822,7 @@ static int intel_setup(struct hci_uart *hu) set_bit(STATE_FIRMWARE_LOADED, &intel->flags); - BT_INFO("%s: Waiting for firmware download to complete", hdev->name); + bt_dev_info(hdev, "Waiting for firmware download to complete"); /* Before switching the device into operational mode and with that * booting the loaded firmware, wait for the bootloader notification @@ -580,19 +839,19 @@ static int intel_setup(struct hci_uart *hu) TASK_INTERRUPTIBLE, msecs_to_jiffies(5000)); if (err == 1) { - BT_ERR("%s: Firmware loading interrupted", hdev->name); + bt_dev_err(hdev, "Firmware loading interrupted"); err = -EINTR; goto done; } if (err) { - BT_ERR("%s: Firmware loading timeout", hdev->name); + bt_dev_err(hdev, "Firmware loading timeout"); err = -ETIMEDOUT; goto done; } if (test_bit(STATE_FIRMWARE_FAILED, &intel->flags)) { - BT_ERR("%s: Firmware loading failed", hdev->name); + bt_dev_err(hdev, "Firmware loading failed"); err = -ENOEXEC; goto done; } @@ -601,7 +860,7 @@ static int intel_setup(struct hci_uart *hu) delta = ktime_sub(rettime, calltime); duration = (unsigned long long) ktime_to_ns(delta) >> 10; - BT_INFO("%s: Firmware loaded in %llu usecs", hdev->name, duration); + bt_dev_info(hdev, "Firmware loaded in %llu usecs", duration); done: release_firmware(fw); @@ -634,7 +893,7 @@ done: * 1 second. However if that happens, then just fail the setup * since something went wrong. */ - BT_INFO("%s: Waiting for device to boot", hdev->name); + bt_dev_info(hdev, "Waiting for device to boot"); err = intel_wait_booting(hu); if (err) @@ -646,7 +905,39 @@ done: delta = ktime_sub(rettime, calltime); duration = (unsigned long long) ktime_to_ns(delta) >> 10; - BT_INFO("%s: Device booted in %llu usecs", hdev->name, duration); + bt_dev_info(hdev, "Device booted in %llu usecs", duration); + + /* Enable LPM if matching pdev with wakeup enabled */ + mutex_lock(&intel_device_list_lock); + list_for_each(p, &intel_device_list) { + struct intel_device *dev = list_entry(p, struct intel_device, + list); + if (hu->tty->dev->parent == dev->pdev->dev.parent) { + if (device_may_wakeup(&dev->pdev->dev)) + idev = dev; + break; + } + } + mutex_unlock(&intel_device_list_lock); + + if (!idev) + goto no_lpm; + + bt_dev_info(hdev, "Enabling LPM"); + + skb = __hci_cmd_sync(hdev, 0xfc8b, sizeof(lpm_param), lpm_param, + HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Failed to enable LPM"); + goto no_lpm; + } + kfree_skb(skb); + + set_bit(STATE_LPM_ENABLED, &intel->flags); + +no_lpm: + /* Ignore errors, device can work without DDC parameters */ + btintel_load_ddc_config(hdev, fwname); skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) @@ -659,7 +950,7 @@ done: return err; } - BT_INFO("%s: Setup complete", hdev->name); + bt_dev_info(hdev, "Setup complete"); clear_bit(STATE_BOOTLOADER, &intel->flags); @@ -708,10 +999,71 @@ recv: return hci_recv_frame(hdev, skb); } +static void intel_recv_lpm_notify(struct hci_dev *hdev, int value) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct intel_data *intel = hu->priv; + + bt_dev_dbg(hdev, "TX idle notification (%d)", value); + + if (value) { + set_bit(STATE_TX_ACTIVE, &intel->flags); + schedule_work(&intel->busy_work); + } else { + clear_bit(STATE_TX_ACTIVE, &intel->flags); + } +} + +static int intel_recv_lpm(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_lpm_pkt *lpm = (void *)skb->data; + struct hci_uart *hu = hci_get_drvdata(hdev); + struct intel_data *intel = hu->priv; + + switch (lpm->opcode) { + case LPM_OP_TX_NOTIFY: + if (lpm->dlen < 1) { + bt_dev_err(hu->hdev, "Invalid LPM notification packet"); + break; + } + intel_recv_lpm_notify(hdev, lpm->data[0]); + break; + case LPM_OP_SUSPEND_ACK: + set_bit(STATE_SUSPENDED, &intel->flags); + if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags)) { + smp_mb__after_atomic(); + wake_up_bit(&intel->flags, STATE_LPM_TRANSACTION); + } + break; + case LPM_OP_RESUME_ACK: + clear_bit(STATE_SUSPENDED, &intel->flags); + if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags)) { + smp_mb__after_atomic(); + wake_up_bit(&intel->flags, STATE_LPM_TRANSACTION); + } + break; + default: + bt_dev_err(hdev, "Unknown LPM opcode (%02x)", lpm->opcode); + break; + } + + kfree_skb(skb); + + return 0; +} + +#define INTEL_RECV_LPM \ + .type = HCI_LPM_PKT, \ + .hlen = HCI_LPM_HDR_SIZE, \ + .loff = 1, \ + .lsize = 1, \ + .maxlen = HCI_LPM_MAX_SIZE + static const struct h4_recv_pkt intel_recv_pkts[] = { - { H4_RECV_ACL, .recv = hci_recv_frame }, - { H4_RECV_SCO, .recv = hci_recv_frame }, - { H4_RECV_EVENT, .recv = intel_recv_event }, + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = intel_recv_event }, + { INTEL_RECV_LPM, .recv = intel_recv_lpm }, }; static int intel_recv(struct hci_uart *hu, const void *data, int count) @@ -726,7 +1078,7 @@ static int intel_recv(struct hci_uart *hu, const void *data, int count) ARRAY_SIZE(intel_recv_pkts)); if (IS_ERR(intel->rx_skb)) { int err = PTR_ERR(intel->rx_skb); - BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err); intel->rx_skb = NULL; return err; } @@ -737,9 +1089,27 @@ static int intel_recv(struct hci_uart *hu, const void *data, int count) static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct intel_data *intel = hu->priv; + struct list_head *p; BT_DBG("hu %p skb %p", hu, skb); + /* Be sure our controller is resumed and potential LPM transaction + * completed before enqueuing any packet. + */ + mutex_lock(&intel_device_list_lock); + list_for_each(p, &intel_device_list) { + struct intel_device *idev = list_entry(p, struct intel_device, + list); + + if (hu->tty->dev->parent == idev->pdev->dev.parent) { + pm_runtime_get_sync(&idev->pdev->dev); + pm_runtime_mark_last_busy(&idev->pdev->dev); + pm_runtime_put_autosuspend(&idev->pdev->dev); + break; + } + } + mutex_unlock(&intel_device_list_lock); + skb_queue_tail(&intel->txq, skb); return 0; @@ -795,24 +1165,61 @@ static const struct acpi_device_id intel_acpi_match[] = { { }, }; MODULE_DEVICE_TABLE(acpi, intel_acpi_match); +#endif -static int intel_acpi_probe(struct intel_device *idev) +#ifdef CONFIG_PM +static int intel_suspend_device(struct device *dev) { - const struct acpi_device_id *id; + struct intel_device *idev = dev_get_drvdata(dev); - id = acpi_match_device(intel_acpi_match, &idev->pdev->dev); - if (!id) - return -ENODEV; + mutex_lock(&idev->hu_lock); + if (idev->hu) + intel_lpm_suspend(idev->hu); + mutex_unlock(&idev->hu_lock); return 0; } -#else -static int intel_acpi_probe(struct intel_device *idev) + +static int intel_resume_device(struct device *dev) +{ + struct intel_device *idev = dev_get_drvdata(dev); + + mutex_lock(&idev->hu_lock); + if (idev->hu) + intel_lpm_resume(idev->hu); + mutex_unlock(&idev->hu_lock); + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int intel_suspend(struct device *dev) +{ + struct intel_device *idev = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(idev->irq); + + return intel_suspend_device(dev); +} + +static int intel_resume(struct device *dev) { - return -ENODEV; + struct intel_device *idev = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(idev->irq); + + return intel_resume_device(dev); } #endif +static const struct dev_pm_ops intel_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) + SET_RUNTIME_PM_OPS(intel_suspend_device, intel_resume_device, NULL) +}; + static int intel_probe(struct platform_device *pdev) { struct intel_device *idev; @@ -821,15 +1228,9 @@ static int intel_probe(struct platform_device *pdev) if (!idev) return -ENOMEM; - idev->pdev = pdev; + mutex_init(&idev->hu_lock); - if (ACPI_HANDLE(&pdev->dev)) { - int err = intel_acpi_probe(idev); - if (err) - return err; - } else { - return -ENODEV; - } + idev->pdev = pdev; idev->reset = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); @@ -838,14 +1239,40 @@ static int intel_probe(struct platform_device *pdev) return PTR_ERR(idev->reset); } + idev->irq = platform_get_irq(pdev, 0); + if (idev->irq < 0) { + struct gpio_desc *host_wake; + + dev_err(&pdev->dev, "No IRQ, falling back to gpio-irq\n"); + + host_wake = devm_gpiod_get_optional(&pdev->dev, "host-wake", + GPIOD_IN); + if (IS_ERR(host_wake)) { + dev_err(&pdev->dev, "Unable to retrieve IRQ\n"); + goto no_irq; + } + + idev->irq = gpiod_to_irq(host_wake); + if (idev->irq < 0) { + dev_err(&pdev->dev, "No corresponding irq for gpio\n"); + goto no_irq; + } + } + + /* Only enable wake-up/irq when controller is powered */ + device_set_wakeup_capable(&pdev->dev, true); + device_wakeup_disable(&pdev->dev); + +no_irq: platform_set_drvdata(pdev, idev); /* Place this instance on the device list */ - spin_lock(&intel_device_list_lock); + mutex_lock(&intel_device_list_lock); list_add_tail(&idev->list, &intel_device_list); - spin_unlock(&intel_device_list_lock); + mutex_unlock(&intel_device_list_lock); - dev_info(&pdev->dev, "registered.\n"); + dev_info(&pdev->dev, "registered, gpio(%d)/irq(%d).\n", + desc_to_gpio(idev->reset), idev->irq); return 0; } @@ -854,9 +1281,11 @@ static int intel_remove(struct platform_device *pdev) { struct intel_device *idev = platform_get_drvdata(pdev); - spin_lock(&intel_device_list_lock); + device_wakeup_disable(&pdev->dev); + + mutex_lock(&intel_device_list_lock); list_del(&idev->list); - spin_unlock(&intel_device_list_lock); + mutex_unlock(&intel_device_list_lock); dev_info(&pdev->dev, "unregistered.\n"); @@ -869,6 +1298,7 @@ static struct platform_driver intel_driver = { .driver = { .name = "hci_intel", .acpi_match_table = ACPI_PTR(intel_acpi_match), + .pm = &intel_pm_ops, }, }; diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 0d5a05a7c1fd..01a83a3f8a1d 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -208,9 +208,6 @@ static int hci_uart_open(struct hci_dev *hdev) BT_DBG("%s %p", hdev->name, hdev); /* Nothing to do for UART driver */ - - set_bit(HCI_RUNNING, &hdev->flags); - return 0; } @@ -241,9 +238,6 @@ static int hci_uart_close(struct hci_dev *hdev) { BT_DBG("hdev %p", hdev); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - hci_uart_flush(hdev); hdev->flush = NULL; return 0; @@ -254,9 +248,6 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_uart *hu = hci_get_drvdata(hdev); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); hu->proto->enqueue(hu, skb); @@ -470,8 +461,6 @@ static int hci_uart_tty_open(struct tty_struct *tty) INIT_WORK(&hu->init_ready, hci_uart_init_work); INIT_WORK(&hu->write_work, hci_uart_write_work); - spin_lock_init(&hu->rx_lock); - /* Flush any pending characters in the driver and line discipline. */ /* FIXME: why is this needed. Note don't use ldisc_ref here as the @@ -569,14 +558,14 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, if (!test_bit(HCI_UART_PROTO_SET, &hu->flags)) return; - spin_lock(&hu->rx_lock); + /* It does not need a lock here as it is already protected by a mutex in + * tty caller + */ hu->proto->recv(hu, data, count); if (hu->hdev) hu->hdev->stat.byte_rx += count; - spin_unlock(&hu->rx_lock); - tty_unthrottle(tty); } diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 6b9b91267959..b4a0393b2862 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -41,13 +41,13 @@ #define HCI_IBS_SLEEP_IND 0xFE #define HCI_IBS_WAKE_IND 0xFD #define HCI_IBS_WAKE_ACK 0xFC -#define HCI_MAX_IBS_SIZE 10 +#define HCI_MAX_IBS_SIZE 10 /* Controller states */ #define STATE_IN_BAND_SLEEP_ENABLED 1 -#define IBS_WAKE_RETRANS_TIMEOUT_MS 100 -#define IBS_TX_IDLE_TIMEOUT_MS 2000 +#define IBS_WAKE_RETRANS_TIMEOUT_MS 100 +#define IBS_TX_IDLE_TIMEOUT_MS 2000 #define BAUDRATE_SETTLE_TIMEOUT_MS 300 /* HCI_IBS transmit side sleep protocol states */ @@ -181,8 +181,8 @@ static void serial_clock_vote(unsigned long vote, struct hci_uart *hu) else __serial_clock_off(hu->tty); - BT_DBG("Vote serial clock %s(%s)", new_vote? "true" : "false", - vote? "true" : "false"); + BT_DBG("Vote serial clock %s(%s)", new_vote ? "true" : "false", + vote ? "true" : "false"); diff = jiffies_to_msecs(jiffies - qca->vote_last_jif); @@ -347,7 +347,7 @@ static void hci_ibs_wake_retrans_timeout(unsigned long arg) struct hci_uart *hu = (struct hci_uart *)arg; struct qca_data *qca = hu->priv; unsigned long flags, retrans_delay; - unsigned long retransmit = 0; + bool retransmit = false; BT_DBG("hu %p wake retransmit timeout in %d state", hu, qca->tx_ibs_state); @@ -358,7 +358,7 @@ static void hci_ibs_wake_retrans_timeout(unsigned long arg) switch (qca->tx_ibs_state) { case HCI_IBS_TX_WAKING: /* No WAKE_ACK, retransmit WAKE */ - retransmit = 1; + retransmit = true; if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) { BT_ERR("Failed to acknowledge device wake up"); break; @@ -821,7 +821,7 @@ static struct sk_buff *qca_dequeue(struct hci_uart *hu) static uint8_t qca_get_baudrate_value(int speed) { - switch(speed) { + switch (speed) { case 9600: return QCA_BAUDRATE_9600; case 19200: diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 495b9ef52bb0..2f7bb35a890e 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -85,7 +85,6 @@ struct hci_uart { struct sk_buff *tx_skb; unsigned long tx_state; - spinlock_t rx_lock; unsigned int init_speed; unsigned int oper_speed; diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 78653db2ef2b..ed888e302bc3 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -55,8 +55,6 @@ struct vhci_data { static int vhci_open_dev(struct hci_dev *hdev) { - set_bit(HCI_RUNNING, &hdev->flags); - return 0; } @@ -64,9 +62,6 @@ static int vhci_close_dev(struct hci_dev *hdev) { struct vhci_data *data = hci_get_drvdata(hdev); - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; - skb_queue_purge(&data->readq); return 0; @@ -85,9 +80,6 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct vhci_data *data = hci_get_drvdata(hdev); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); skb_queue_tail(&data->readq, skb); diff --git a/drivers/char/hw_random/xgene-rng.c b/drivers/char/hw_random/xgene-rng.c index c37cf754a985..3c77645405e5 100644 --- a/drivers/char/hw_random/xgene-rng.c +++ b/drivers/char/hw_random/xgene-rng.c @@ -344,11 +344,12 @@ static int xgene_rng_probe(struct platform_device *pdev) if (IS_ERR(ctx->csr_base)) return PTR_ERR(ctx->csr_base); - ctx->irq = platform_get_irq(pdev, 0); - if (ctx->irq < 0) { + rc = platform_get_irq(pdev, 0); + if (rc < 0) { dev_err(&pdev->dev, "No IRQ resource\n"); - return ctx->irq; + return rc; } + ctx->irq = rc; dev_dbg(&pdev->dev, "APM X-Gene RNG BASE %p ALARM IRQ %d", ctx->csr_base, ctx->irq); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 43e2c3ad6c31..0ebcf449778a 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2437,7 +2437,8 @@ static int __clk_init(struct device *dev, struct clk *clk_user) hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { if (orphan->num_parents && orphan->ops->get_parent) { i = orphan->ops->get_parent(orphan->hw); - if (!strcmp(core->name, orphan->parent_names[i])) + if (i >= 0 && i < orphan->num_parents && + !strcmp(core->name, orphan->parent_names[i])) clk_core_reparent(orphan, core); continue; } diff --git a/drivers/clk/h8300/clk-h8s2678.c b/drivers/clk/h8300/clk-h8s2678.c index 2a38eb4a2552..6cf38dc1c929 100644 --- a/drivers/clk/h8300/clk-h8s2678.c +++ b/drivers/clk/h8300/clk-h8s2678.c @@ -8,6 +8,7 @@ #include <linux/err.h> #include <linux/device.h> #include <linux/of_address.h> +#include <linux/slab.h> static DEFINE_SPINLOCK(clklock); diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig index 2c16807341dc..e43485448612 100644 --- a/drivers/clk/hisilicon/Kconfig +++ b/drivers/clk/hisilicon/Kconfig @@ -1,6 +1,12 @@ config COMMON_CLK_HI6220 bool "Hi6220 Clock Driver" - depends on (ARCH_HISI || COMPILE_TEST) && MAILBOX + depends on ARCH_HISI || COMPILE_TEST default ARCH_HISI help Build the Hisilicon Hi6220 clock driver based on the common clock framework. + +config STUB_CLK_HI6220 + bool "Hi6220 Stub Clock Driver" + depends on COMMON_CLK_HI6220 && MAILBOX + help + Build the Hisilicon Hi6220 stub clock driver. diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index 4a1001a11f04..74dba31590f9 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -7,4 +7,5 @@ obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o -obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o clk-hi6220-stub.o +obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o +obj-$(CONFIG_STUB_CLK_HI6220) += clk-hi6220-stub.o diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index ed02bbc7b11f..abb47608713b 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -716,6 +716,8 @@ static const char *const rk3188_critical_clocks[] __initconst = { "aclk_cpu", "aclk_peri", "hclk_peri", + "pclk_cpu", + "pclk_peri", }; static void __init rk3188_common_clk_init(struct device_node *np) @@ -744,8 +746,6 @@ static void __init rk3188_common_clk_init(struct device_node *np) rockchip_clk_register_branches(common_clk_branches, ARRAY_SIZE(common_clk_branches)); - rockchip_clk_protect_critical(rk3188_critical_clocks, - ARRAY_SIZE(rk3188_critical_clocks)); rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); @@ -765,6 +765,8 @@ static void __init rk3066a_clk_init(struct device_node *np) mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3066_cpuclk_data, rk3066_cpuclk_rates, ARRAY_SIZE(rk3066_cpuclk_rates)); + rockchip_clk_protect_critical(rk3188_critical_clocks, + ARRAY_SIZE(rk3188_critical_clocks)); } CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init); @@ -801,6 +803,9 @@ static void __init rk3188a_clk_init(struct device_node *np) pr_warn("%s: missing clocks to reparent aclk_cpu_pre to gpll\n", __func__); } + + rockchip_clk_protect_critical(rk3188_critical_clocks, + ARRAY_SIZE(rk3188_critical_clocks)); } CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c index 9c5d61e698ef..7e6b783e6eee 100644 --- a/drivers/clk/rockchip/clk-rk3368.c +++ b/drivers/clk/rockchip/clk-rk3368.c @@ -818,6 +818,10 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = { GATE(0, "sclk_timer00", "xin24m", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(24), 0, GFLAGS), }; +static const char *const rk3368_critical_clocks[] __initconst = { + "pclk_pd_pmu", +}; + static void __init rk3368_clk_init(struct device_node *np) { void __iomem *reg_base; @@ -862,6 +866,8 @@ static void __init rk3368_clk_init(struct device_node *np) RK3368_GRF_SOC_STATUS0); rockchip_clk_register_branches(rk3368_clk_branches, ARRAY_SIZE(rk3368_clk_branches)); + rockchip_clk_protect_critical(rk3368_critical_clocks, + ARRAY_SIZE(rk3368_critical_clocks)); rockchip_clk_register_armclk(ARMCLKB, "armclkb", mux_armclkb_p, ARRAY_SIZE(mux_armclkb_p), diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c index 83ccf142ff2a..576cd0354d48 100644 --- a/drivers/clk/st/clkgen-fsyn.c +++ b/drivers/clk/st/clkgen-fsyn.c @@ -307,7 +307,7 @@ static const struct clkgen_quadfs_data st_fs660c32_F_416 = { .get_rate = clk_fs660c32_dig_get_rate, }; -static const struct clkgen_quadfs_data st_fs660c32_C_407 = { +static const struct clkgen_quadfs_data st_fs660c32_C = { .nrst_present = true, .nrst = { CLKGEN_FIELD(0x2f0, 0x1, 0), CLKGEN_FIELD(0x2f0, 0x1, 1), @@ -350,7 +350,7 @@ static const struct clkgen_quadfs_data st_fs660c32_C_407 = { .get_rate = clk_fs660c32_dig_get_rate, }; -static const struct clkgen_quadfs_data st_fs660c32_D_407 = { +static const struct clkgen_quadfs_data st_fs660c32_D = { .nrst_present = true, .nrst = { CLKGEN_FIELD(0x2a0, 0x1, 0), CLKGEN_FIELD(0x2a0, 0x1, 1), @@ -1077,11 +1077,11 @@ static const struct of_device_id quadfs_of_match[] = { }, { .compatible = "st,stih407-quadfs660-C", - .data = &st_fs660c32_C_407 + .data = &st_fs660c32_C }, { .compatible = "st,stih407-quadfs660-D", - .data = &st_fs660c32_D_407 + .data = &st_fs660c32_D }, {} }; diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index 47a38a994cac..b2a332cf8985 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -193,7 +193,7 @@ static const struct clkgen_pll_data st_pll3200c32_407_a0 = { .ops = &stm_pll3200c32_ops, }; -static const struct clkgen_pll_data st_pll3200c32_407_c0_0 = { +static const struct clkgen_pll_data st_pll3200c32_cx_0 = { /* 407 C0 PLL0 */ .pdn_status = CLKGEN_FIELD(0x2a0, 0x1, 8), .locked_status = CLKGEN_FIELD(0x2a0, 0x1, 24), @@ -205,7 +205,7 @@ static const struct clkgen_pll_data st_pll3200c32_407_c0_0 = { .ops = &stm_pll3200c32_ops, }; -static const struct clkgen_pll_data st_pll3200c32_407_c0_1 = { +static const struct clkgen_pll_data st_pll3200c32_cx_1 = { /* 407 C0 PLL1 */ .pdn_status = CLKGEN_FIELD(0x2c8, 0x1, 8), .locked_status = CLKGEN_FIELD(0x2c8, 0x1, 24), @@ -624,12 +624,12 @@ static const struct of_device_id c32_pll_of_match[] = { .data = &st_pll3200c32_407_a0, }, { - .compatible = "st,stih407-plls-c32-c0_0", - .data = &st_pll3200c32_407_c0_0, + .compatible = "st,plls-c32-cx_0", + .data = &st_pll3200c32_cx_0, }, { - .compatible = "st,stih407-plls-c32-c0_1", - .data = &st_pll3200c32_407_c0_1, + .compatible = "st,plls-c32-cx_1", + .data = &st_pll3200c32_cx_1, }, { .compatible = "st,stih407-plls-c32-a9", diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c index c2ff859ee0e8..c4e3a52e225b 100644 --- a/drivers/clk/tegra/clk-dfll.c +++ b/drivers/clk/tegra/clk-dfll.c @@ -682,11 +682,17 @@ static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate) struct dev_pm_opp *opp; int i, uv; + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate); - if (IS_ERR(opp)) + if (IS_ERR(opp)) { + rcu_read_unlock(); return PTR_ERR(opp); + } uv = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + for (i = 0; i < td->i2c_lut_size; i++) { if (regulator_list_voltage(td->vdd_reg, td->i2c_lut[i]) == uv) return i; diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 5f498d9f1825..cd0391e46c6d 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -84,6 +84,7 @@ config ARM_KIRKWOOD_CPUFREQ config ARM_MT8173_CPUFREQ bool "Mediatek MT8173 CPUFreq support" depends on ARCH_MEDIATEK && REGULATOR + depends on !CPU_THERMAL || THERMAL=y select PM_OPP help This adds the CPUFreq driver support for Mediatek MT8173 SoC. diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 15b921a9248c..798277227de7 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -375,12 +375,11 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu) pr_debug("get_cur_freq_on_cpu (%d)\n", cpu); - policy = cpufreq_cpu_get(cpu); + policy = cpufreq_cpu_get_raw(cpu); if (unlikely(!policy)) return 0; data = policy->driver_data; - cpufreq_cpu_put(policy); if (unlikely(!data || !data->freq_table)) return 0; diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index c3583cdfadbd..7c0d70e2a861 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -196,6 +196,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) struct device *cpu_dev; struct regulator *cpu_reg; struct clk *cpu_clk; + struct dev_pm_opp *suspend_opp; unsigned long min_uV = ~0, max_uV = 0; unsigned int transition_latency; bool need_update = false; @@ -239,6 +240,17 @@ static int cpufreq_init(struct cpufreq_policy *policy) */ of_cpumask_init_opp_table(policy->cpus); + /* + * But we need OPP table to function so if it is not there let's + * give platform code chance to provide it for us. + */ + ret = dev_pm_opp_get_opp_count(cpu_dev); + if (ret <= 0) { + pr_debug("OPP table is not ready, deferring probe\n"); + ret = -EPROBE_DEFER; + goto out_free_opp; + } + if (need_update) { struct cpufreq_dt_platform_data *pd = cpufreq_get_driver_data(); @@ -249,24 +261,16 @@ static int cpufreq_init(struct cpufreq_policy *policy) * OPP tables are initialized only for policy->cpu, do it for * others as well. */ - set_cpus_sharing_opps(cpu_dev, policy->cpus); + ret = set_cpus_sharing_opps(cpu_dev, policy->cpus); + if (ret) + dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", + __func__, ret); of_property_read_u32(np, "clock-latency", &transition_latency); } else { transition_latency = dev_pm_opp_get_max_clock_latency(cpu_dev); } - /* - * But we need OPP table to function so if it is not there let's - * give platform code chance to provide it for us. - */ - ret = dev_pm_opp_get_opp_count(cpu_dev); - if (ret <= 0) { - pr_debug("OPP table is not ready, deferring probe\n"); - ret = -EPROBE_DEFER; - goto out_free_opp; - } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; @@ -300,7 +304,8 @@ static int cpufreq_init(struct cpufreq_policy *policy) rcu_read_unlock(); tol_uV = opp_uV * priv->voltage_tolerance / 100; - if (regulator_is_supported_voltage(cpu_reg, opp_uV, + if (regulator_is_supported_voltage(cpu_reg, + opp_uV - tol_uV, opp_uV + tol_uV)) { if (opp_uV < min_uV) min_uV = opp_uV; @@ -329,6 +334,13 @@ static int cpufreq_init(struct cpufreq_policy *policy) policy->driver_data = priv; policy->clk = cpu_clk; + + rcu_read_lock(); + suspend_opp = dev_pm_opp_get_suspend_opp(cpu_dev); + if (suspend_opp) + policy->suspend_freq = dev_pm_opp_get_freq(suspend_opp) / 1000; + rcu_read_unlock(); + ret = cpufreq_table_validate_and_show(policy, freq_table); if (ret) { dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__, @@ -419,6 +431,7 @@ static struct cpufreq_driver dt_cpufreq_driver = { .ready = cpufreq_ready, .name = "cpufreq-dt", .attr = cpufreq_dt_attr, + .suspend = cpufreq_generic_suspend, }; static int dt_cpufreq_probe(struct platform_device *pdev) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index b3d9368339af..ef5ed9470de9 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -238,13 +238,13 @@ int cpufreq_generic_init(struct cpufreq_policy *policy, } EXPORT_SYMBOL_GPL(cpufreq_generic_init); -/* Only for cpufreq core internal use */ struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu) { struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); return policy && cpumask_test_cpu(cpu, policy->cpus) ? policy : NULL; } +EXPORT_SYMBOL_GPL(cpufreq_cpu_get_raw); unsigned int cpufreq_generic_get(unsigned int cpu) { @@ -1626,8 +1626,8 @@ int cpufreq_generic_suspend(struct cpufreq_policy *policy) int ret; if (!policy->suspend_freq) { - pr_err("%s: suspend_freq can't be zero\n", __func__); - return -EINVAL; + pr_debug("%s: suspend_freq not defined\n", __func__); + return 0; } pr_debug("%s: Setting suspend-freq: %u\n", __func__, @@ -2031,8 +2031,7 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, if (!try_module_get(policy->governor->owner)) return -EINVAL; - pr_debug("__cpufreq_governor for CPU %u, event %u\n", - policy->cpu, event); + pr_debug("%s: for CPU %u, event %u\n", __func__, policy->cpu, event); mutex_lock(&cpufreq_governor_lock); if ((policy->governor_enabled && event == CPUFREQ_GOV_START) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index cddc61939a86..3af9dd7332e6 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -260,24 +260,31 @@ static inline void update_turbo_state(void) cpu->pstate.max_pstate == cpu->pstate.turbo_pstate); } -#define PCT_TO_HWP(x) (x * 255 / 100) static void intel_pstate_hwp_set(void) { - int min, max, cpu; - u64 value, freq; + int min, hw_min, max, hw_max, cpu, range, adj_range; + u64 value, cap; + + rdmsrl(MSR_HWP_CAPABILITIES, cap); + hw_min = HWP_LOWEST_PERF(cap); + hw_max = HWP_HIGHEST_PERF(cap); + range = hw_max - hw_min; get_online_cpus(); for_each_online_cpu(cpu) { rdmsrl_on_cpu(cpu, MSR_HWP_REQUEST, &value); - min = PCT_TO_HWP(limits.min_perf_pct); + adj_range = limits.min_perf_pct * range / 100; + min = hw_min + adj_range; value &= ~HWP_MIN_PERF(~0L); value |= HWP_MIN_PERF(min); - max = PCT_TO_HWP(limits.max_perf_pct); + adj_range = limits.max_perf_pct * range / 100; + max = hw_min + adj_range; if (limits.no_turbo) { - rdmsrl( MSR_HWP_CAPABILITIES, freq); - max = HWP_GUARANTEED_PERF(freq); + hw_max = HWP_GUARANTEED_PERF(cap); + if (hw_max < max) + max = hw_max; } value &= ~HWP_MAX_PERF(~0L); @@ -423,6 +430,8 @@ static ssize_t store_max_perf_pct(struct kobject *a, struct attribute *b, limits.max_sysfs_pct = clamp_t(int, input, 0 , 100); limits.max_perf_pct = min(limits.max_policy_pct, limits.max_sysfs_pct); + limits.max_perf_pct = max(limits.min_policy_pct, limits.max_perf_pct); + limits.max_perf_pct = max(limits.min_perf_pct, limits.max_perf_pct); limits.max_perf = div_fp(int_tofp(limits.max_perf_pct), int_tofp(100)); if (hwp_active) @@ -442,6 +451,8 @@ static ssize_t store_min_perf_pct(struct kobject *a, struct attribute *b, limits.min_sysfs_pct = clamp_t(int, input, 0 , 100); limits.min_perf_pct = max(limits.min_policy_pct, limits.min_sysfs_pct); + limits.min_perf_pct = min(limits.max_policy_pct, limits.min_perf_pct); + limits.min_perf_pct = min(limits.max_perf_pct, limits.min_perf_pct); limits.min_perf = div_fp(int_tofp(limits.min_perf_pct), int_tofp(100)); if (hwp_active) @@ -989,12 +1000,19 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) limits.min_policy_pct = (policy->min * 100) / policy->cpuinfo.max_freq; limits.min_policy_pct = clamp_t(int, limits.min_policy_pct, 0 , 100); - limits.min_perf_pct = max(limits.min_policy_pct, limits.min_sysfs_pct); - limits.min_perf = div_fp(int_tofp(limits.min_perf_pct), int_tofp(100)); - limits.max_policy_pct = (policy->max * 100) / policy->cpuinfo.max_freq; limits.max_policy_pct = clamp_t(int, limits.max_policy_pct, 0 , 100); + + /* Normalize user input to [min_policy_pct, max_policy_pct] */ + limits.min_perf_pct = max(limits.min_policy_pct, limits.min_sysfs_pct); + limits.min_perf_pct = min(limits.max_policy_pct, limits.min_perf_pct); limits.max_perf_pct = min(limits.max_policy_pct, limits.max_sysfs_pct); + limits.max_perf_pct = max(limits.min_policy_pct, limits.max_perf_pct); + + /* Make sure min_perf_pct <= max_perf_pct */ + limits.min_perf_pct = min(limits.max_perf_pct, limits.min_perf_pct); + + limits.min_perf = div_fp(int_tofp(limits.min_perf_pct), int_tofp(100)); limits.max_perf = div_fp(int_tofp(limits.max_perf_pct), int_tofp(100)); if (hwp_active) diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c index 1523e2d745eb..344058f8501a 100644 --- a/drivers/cpuidle/coupled.c +++ b/drivers/cpuidle/coupled.c @@ -187,6 +187,28 @@ bool cpuidle_state_is_coupled(struct cpuidle_driver *drv, int state) } /** + * cpuidle_coupled_state_verify - check if the coupled states are correctly set. + * @drv: struct cpuidle_driver for the platform + * + * Returns 0 for valid state values, a negative error code otherwise: + * * -EINVAL if any coupled state(safe_state_index) is wrongly set. + */ +int cpuidle_coupled_state_verify(struct cpuidle_driver *drv) +{ + int i; + + for (i = drv->state_count - 1; i >= 0; i--) { + if (cpuidle_state_is_coupled(drv, i) && + (drv->safe_state_index == i || + drv->safe_state_index < 0 || + drv->safe_state_index >= drv->state_count)) + return -EINVAL; + } + + return 0; +} + +/** * cpuidle_coupled_set_ready - mark a cpu as ready * @coupled: the struct coupled that contains the current cpu */ diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h index 178c5ad3d568..f87f399b0540 100644 --- a/drivers/cpuidle/cpuidle.h +++ b/drivers/cpuidle/cpuidle.h @@ -35,6 +35,7 @@ extern void cpuidle_remove_sysfs(struct cpuidle_device *dev); #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED bool cpuidle_state_is_coupled(struct cpuidle_driver *drv, int state); +int cpuidle_coupled_state_verify(struct cpuidle_driver *drv); int cpuidle_enter_state_coupled(struct cpuidle_device *dev, struct cpuidle_driver *drv, int next_state); int cpuidle_coupled_register_device(struct cpuidle_device *dev); @@ -46,6 +47,11 @@ bool cpuidle_state_is_coupled(struct cpuidle_driver *drv, int state) return false; } +static inline int cpuidle_coupled_state_verify(struct cpuidle_driver *drv) +{ + return 0; +} + static inline int cpuidle_enter_state_coupled(struct cpuidle_device *dev, struct cpuidle_driver *drv, int next_state) { diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 5db147859b90..389ade4572be 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -227,6 +227,10 @@ static int __cpuidle_register_driver(struct cpuidle_driver *drv) if (!drv || !drv->state_count) return -EINVAL; + ret = cpuidle_coupled_state_verify(drv); + if (ret) + return ret; + if (cpuidle_disabled()) return -ENODEV; diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 07bc7aa6b224..d234719065a5 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -461,7 +461,7 @@ config CRYPTO_DEV_QCE config CRYPTO_DEV_VMX bool "Support for VMX cryptographic acceleration instructions" - depends on PPC64 + depends on PPC64 && VSX help Support for VMX cryptographic acceleration instructions. diff --git a/drivers/crypto/marvell/cesa.h b/drivers/crypto/marvell/cesa.h index b60698b30d30..bc2a55bc35e4 100644 --- a/drivers/crypto/marvell/cesa.h +++ b/drivers/crypto/marvell/cesa.h @@ -687,6 +687,33 @@ static inline u32 mv_cesa_get_int_mask(struct mv_cesa_engine *engine) int mv_cesa_queue_req(struct crypto_async_request *req); +/* + * Helper function that indicates whether a crypto request needs to be + * cleaned up or not after being enqueued using mv_cesa_queue_req(). + */ +static inline int mv_cesa_req_needs_cleanup(struct crypto_async_request *req, + int ret) +{ + /* + * The queue still had some space, the request was queued + * normally, so there's no need to clean it up. + */ + if (ret == -EINPROGRESS) + return false; + + /* + * The queue had not space left, but since the request is + * flagged with CRYPTO_TFM_REQ_MAY_BACKLOG, it was added to + * the backlog and will be processed later. There's no need to + * clean it up. + */ + if (ret == -EBUSY && req->flags & CRYPTO_TFM_REQ_MAY_BACKLOG) + return false; + + /* Request wasn't queued, we need to clean it up */ + return true; +} + /* TDMA functions */ static inline void mv_cesa_req_dma_iter_init(struct mv_cesa_dma_iter *iter, diff --git a/drivers/crypto/marvell/cipher.c b/drivers/crypto/marvell/cipher.c index 0745cf3b9c0e..3df2f4e7adb2 100644 --- a/drivers/crypto/marvell/cipher.c +++ b/drivers/crypto/marvell/cipher.c @@ -189,7 +189,6 @@ static inline void mv_cesa_ablkcipher_prepare(struct crypto_async_request *req, { struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req); struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq); - creq->req.base.engine = engine; if (creq->req.base.type == CESA_DMA_REQ) @@ -431,7 +430,7 @@ static int mv_cesa_des_op(struct ablkcipher_request *req, return ret; ret = mv_cesa_queue_req(&req->base); - if (ret && ret != -EINPROGRESS) + if (mv_cesa_req_needs_cleanup(&req->base, ret)) mv_cesa_ablkcipher_cleanup(req); return ret; @@ -551,7 +550,7 @@ static int mv_cesa_des3_op(struct ablkcipher_request *req, return ret; ret = mv_cesa_queue_req(&req->base); - if (ret && ret != -EINPROGRESS) + if (mv_cesa_req_needs_cleanup(&req->base, ret)) mv_cesa_ablkcipher_cleanup(req); return ret; @@ -693,7 +692,7 @@ static int mv_cesa_aes_op(struct ablkcipher_request *req, return ret; ret = mv_cesa_queue_req(&req->base); - if (ret && ret != -EINPROGRESS) + if (mv_cesa_req_needs_cleanup(&req->base, ret)) mv_cesa_ablkcipher_cleanup(req); return ret; diff --git a/drivers/crypto/marvell/hash.c b/drivers/crypto/marvell/hash.c index ae9272eb9c1a..e8d0d7128137 100644 --- a/drivers/crypto/marvell/hash.c +++ b/drivers/crypto/marvell/hash.c @@ -739,10 +739,8 @@ static int mv_cesa_ahash_update(struct ahash_request *req) return 0; ret = mv_cesa_queue_req(&req->base); - if (ret && ret != -EINPROGRESS) { + if (mv_cesa_req_needs_cleanup(&req->base, ret)) mv_cesa_ahash_cleanup(req); - return ret; - } return ret; } @@ -766,7 +764,7 @@ static int mv_cesa_ahash_final(struct ahash_request *req) return 0; ret = mv_cesa_queue_req(&req->base); - if (ret && ret != -EINPROGRESS) + if (mv_cesa_req_needs_cleanup(&req->base, ret)) mv_cesa_ahash_cleanup(req); return ret; @@ -791,7 +789,7 @@ static int mv_cesa_ahash_finup(struct ahash_request *req) return 0; ret = mv_cesa_queue_req(&req->base); - if (ret && ret != -EINPROGRESS) + if (mv_cesa_req_needs_cleanup(&req->base, ret)) mv_cesa_ahash_cleanup(req); return ret; diff --git a/drivers/crypto/qat/qat_common/adf_aer.c b/drivers/crypto/qat/qat_common/adf_aer.c index a57b4194de28..0a5ca0ba5d64 100644 --- a/drivers/crypto/qat/qat_common/adf_aer.c +++ b/drivers/crypto/qat/qat_common/adf_aer.c @@ -88,6 +88,9 @@ static void adf_dev_restore(struct adf_accel_dev *accel_dev) struct pci_dev *parent = pdev->bus->self; uint16_t bridge_ctl = 0; + if (accel_dev->is_vf) + return; + dev_info(&GET_DEV(accel_dev), "Resetting device qat_dev%d\n", accel_dev->accel_id); diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c b/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c index e070c316e8b7..a19ee127edca 100644 --- a/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c +++ b/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c @@ -104,7 +104,7 @@ static int sun4i_ss_opti_poll(struct ablkcipher_request *areq) sg_miter_next(&mo); oo = 0; } - } while (mo.length > 0); + } while (oleft > 0); if (areq->info) { for (i = 0; i < 4 && i < ivsize / 4; i++) { diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index ca1b362d77e2..3927ed9fdbd5 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -53,7 +53,7 @@ static struct devfreq *find_device_devfreq(struct device *dev) { struct devfreq *tmp_devfreq; - if (unlikely(IS_ERR_OR_NULL(dev))) { + if (IS_ERR_OR_NULL(dev)) { pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); return ERR_PTR(-EINVAL); } @@ -133,7 +133,7 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) { struct devfreq_governor *tmp_governor; - if (unlikely(IS_ERR_OR_NULL(name))) { + if (IS_ERR_OR_NULL(name)) { pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); return ERR_PTR(-EINVAL); } @@ -177,10 +177,10 @@ int update_devfreq(struct devfreq *devfreq) return err; /* - * Adjust the freuqency with user freq and QoS. + * Adjust the frequency with user freq and QoS. * - * List from the highest proiority - * max_freq (probably called by thermal when it's too hot) + * List from the highest priority + * max_freq * min_freq */ @@ -482,7 +482,7 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->profile->max_state * devfreq->profile->max_state, GFP_KERNEL); - devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned int) * + devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned long) * devfreq->profile->max_state, GFP_KERNEL); devfreq->last_stat_updated = jiffies; diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c index f9901f52a225..f312485f1451 100644 --- a/drivers/devfreq/event/exynos-ppmu.c +++ b/drivers/devfreq/event/exynos-ppmu.c @@ -319,7 +319,8 @@ static int exynos_ppmu_v2_get_event(struct devfreq_event_dev *edev, case PPMU_PMNCNT3: pmcnt_high = __raw_readl(info->ppmu.base + PPMU_V2_PMCNT3_HIGH); pmcnt_low = __raw_readl(info->ppmu.base + PPMU_V2_PMCNT3_LOW); - load_count = (u64)((pmcnt_high & 0xff) << 32) + (u64)pmcnt_low; + load_count = ((u64)((pmcnt_high & 0xff)) << 32) + + (u64)pmcnt_low; break; } edata->load_count = load_count; diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index 0720ba84ca92..ae72ba5e78df 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -21,17 +21,20 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, unsigned long *freq) { - struct devfreq_dev_status stat; - int err = df->profile->get_dev_status(df->dev.parent, &stat); + int err; + struct devfreq_dev_status *stat; unsigned long long a, b; unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD; unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL; struct devfreq_simple_ondemand_data *data = df->data; unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX; + err = devfreq_update_stats(df); if (err) return err; + stat = &df->last_status; + if (data) { if (data->upthreshold) dfso_upthreshold = data->upthreshold; @@ -43,41 +46,41 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, return -EINVAL; /* Assume MAX if it is going to be divided by zero */ - if (stat.total_time == 0) { + if (stat->total_time == 0) { *freq = max; return 0; } /* Prevent overflow */ - if (stat.busy_time >= (1 << 24) || stat.total_time >= (1 << 24)) { - stat.busy_time >>= 7; - stat.total_time >>= 7; + if (stat->busy_time >= (1 << 24) || stat->total_time >= (1 << 24)) { + stat->busy_time >>= 7; + stat->total_time >>= 7; } /* Set MAX if it's busy enough */ - if (stat.busy_time * 100 > - stat.total_time * dfso_upthreshold) { + if (stat->busy_time * 100 > + stat->total_time * dfso_upthreshold) { *freq = max; return 0; } /* Set MAX if we do not know the initial frequency */ - if (stat.current_frequency == 0) { + if (stat->current_frequency == 0) { *freq = max; return 0; } /* Keep the current frequency */ - if (stat.busy_time * 100 > - stat.total_time * (dfso_upthreshold - dfso_downdifferential)) { - *freq = stat.current_frequency; + if (stat->busy_time * 100 > + stat->total_time * (dfso_upthreshold - dfso_downdifferential)) { + *freq = stat->current_frequency; return 0; } /* Set the desired frequency based on the load */ - a = stat.busy_time; - a *= stat.current_frequency; - b = div_u64(a, stat.total_time); + a = stat->busy_time; + a *= stat->current_frequency; + b = div_u64(a, stat->total_time); b *= 100; b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2)); *freq = (unsigned long) b; diff --git a/drivers/devfreq/tegra-devfreq.c b/drivers/devfreq/tegra-devfreq.c index 13a1a6e8108c..848b93ee930f 100644 --- a/drivers/devfreq/tegra-devfreq.c +++ b/drivers/devfreq/tegra-devfreq.c @@ -541,18 +541,20 @@ static struct devfreq_dev_profile tegra_devfreq_profile = { static int tegra_governor_get_target(struct devfreq *devfreq, unsigned long *freq) { - struct devfreq_dev_status stat; + struct devfreq_dev_status *stat; struct tegra_devfreq *tegra; struct tegra_devfreq_device *dev; unsigned long target_freq = 0; unsigned int i; int err; - err = devfreq->profile->get_dev_status(devfreq->dev.parent, &stat); + err = devfreq_update_stats(devfreq); if (err) return err; - tegra = stat.private_data; + stat = &devfreq->last_status; + + tegra = stat->private_data; for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { dev = &tegra->devices[i]; diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c index 4768a829253a..2bf37e68ad0f 100644 --- a/drivers/dma/ipu/ipu_irq.c +++ b/drivers/dma/ipu/ipu_irq.c @@ -266,7 +266,7 @@ int ipu_irq_unmap(unsigned int source) } /* Chained IRQ handler for IPU function and error interrupt */ -static void ipu_irq_handler(unsigned int __irq, struct irq_desc *desc) +static void ipu_irq_handler(struct irq_desc *desc) { struct ipu *ipu = irq_desc_get_handler_data(desc); u32 status; diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index ca7831168298..cf1268ddef0c 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -280,6 +280,7 @@ struct sbridge_info { u8 max_interleave; u8 (*get_node_id)(struct sbridge_pvt *pvt); enum mem_type (*get_memory_type)(struct sbridge_pvt *pvt); + enum dev_type (*get_width)(struct sbridge_pvt *pvt, u32 mtr); struct pci_dev *pci_vtd; }; @@ -471,6 +472,9 @@ static const struct pci_id_table pci_dev_descr_ibridge_table[] = { #define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD2 0x2f6c #define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD3 0x2f6d #define PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO0 0x2fbd +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO1 0x2fbf +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO2 0x2fb9 +#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO3 0x2fbb static const struct pci_id_descr pci_dev_descr_haswell[] = { /* first item must be the HA */ { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0, 0) }, @@ -488,6 +492,9 @@ static const struct pci_id_descr pci_dev_descr_haswell[] = { { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD3, 1) }, { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO0, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO1, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO2, 1) }, + { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO3, 1) }, { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TA, 1) }, { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_THERMAL, 1) }, @@ -762,6 +769,49 @@ out: return mtype; } +static enum dev_type sbridge_get_width(struct sbridge_pvt *pvt, u32 mtr) +{ + /* there's no way to figure out */ + return DEV_UNKNOWN; +} + +static enum dev_type __ibridge_get_width(u32 mtr) +{ + enum dev_type type; + + switch (mtr) { + case 3: + type = DEV_UNKNOWN; + break; + case 2: + type = DEV_X16; + break; + case 1: + type = DEV_X8; + break; + case 0: + type = DEV_X4; + break; + } + + return type; +} + +static enum dev_type ibridge_get_width(struct sbridge_pvt *pvt, u32 mtr) +{ + /* + * ddr3_width on the documentation but also valid for DDR4 on + * Haswell + */ + return __ibridge_get_width(GET_BITFIELD(mtr, 7, 8)); +} + +static enum dev_type broadwell_get_width(struct sbridge_pvt *pvt, u32 mtr) +{ + /* ddr3_width on the documentation but also valid for DDR4 */ + return __ibridge_get_width(GET_BITFIELD(mtr, 8, 9)); +} + static u8 get_node_id(struct sbridge_pvt *pvt) { u32 reg; @@ -966,17 +1016,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) dimm->nr_pages = npages; dimm->grain = 32; - switch (banks) { - case 16: - dimm->dtype = DEV_X16; - break; - case 8: - dimm->dtype = DEV_X8; - break; - case 4: - dimm->dtype = DEV_X4; - break; - } + dimm->dtype = pvt->info.get_width(pvt, mtr); dimm->mtype = mtype; dimm->edac_mode = mode; snprintf(dimm->label, sizeof(dimm->label), @@ -1869,7 +1909,11 @@ static int haswell_mci_bind_devs(struct mem_ctl_info *mci, } break; case PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO0: - pvt->pci_ddrio = pdev; + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO1: + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO2: + case PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO3: + if (!pvt->pci_ddrio) + pvt->pci_ddrio = pdev; break; case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1: pvt->pci_ha1 = pdev; @@ -2361,6 +2405,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) pvt->info.interleave_list = ibridge_interleave_list; pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list); pvt->info.interleave_pkg = ibridge_interleave_pkg; + pvt->info.get_width = ibridge_get_width; mci->ctl_name = kasprintf(GFP_KERNEL, "Ivy Bridge Socket#%d", mci->mc_idx); /* Store pci devices at mci for faster access */ @@ -2380,6 +2425,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) pvt->info.interleave_list = sbridge_interleave_list; pvt->info.max_interleave = ARRAY_SIZE(sbridge_interleave_list); pvt->info.interleave_pkg = sbridge_interleave_pkg; + pvt->info.get_width = sbridge_get_width; mci->ctl_name = kasprintf(GFP_KERNEL, "Sandy Bridge Socket#%d", mci->mc_idx); /* Store pci devices at mci for faster access */ @@ -2399,6 +2445,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) pvt->info.interleave_list = ibridge_interleave_list; pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list); pvt->info.interleave_pkg = ibridge_interleave_pkg; + pvt->info.get_width = ibridge_get_width; mci->ctl_name = kasprintf(GFP_KERNEL, "Haswell Socket#%d", mci->mc_idx); /* Store pci devices at mci for faster access */ @@ -2418,6 +2465,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type) pvt->info.interleave_list = ibridge_interleave_list; pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list); pvt->info.interleave_pkg = ibridge_interleave_pkg; + pvt->info.get_width = broadwell_get_width; mci->ctl_name = kasprintf(GFP_KERNEL, "Broadwell Socket#%d", mci->mc_idx); /* Store pci devices at mci for faster access */ diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index a07addde297b..8dd0af1d50bc 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -159,7 +159,7 @@ static int find_cable_index_by_name(struct extcon_dev *edev, const char *name) static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached) { if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) { - *attached = new ? true : false; + *attached = ((new >> idx) & 0x1) ? true : false; return true; } diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index d8de6a8dd4de..665efca59487 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -139,6 +139,14 @@ config QCOM_SCM bool depends on ARM || ARM64 +config QCOM_SCM_32 + def_bool y + depends on QCOM_SCM && ARM + +config QCOM_SCM_64 + def_bool y + depends on QCOM_SCM && ARM64 + source "drivers/firmware/broadcom/Kconfig" source "drivers/firmware/google/Kconfig" source "drivers/firmware/efi/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 000830fc6707..2ee83474a3c1 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -13,7 +13,8 @@ obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o obj-$(CONFIG_QCOM_SCM) += qcom_scm.o -obj-$(CONFIG_QCOM_SCM) += qcom_scm-32.o +obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o +obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) obj-y += broadcom/ diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index e334a01cf92f..6b6548fda089 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -5,10 +5,6 @@ /* error code which can't be mistaken for valid address */ #define EFI_ERROR (~0UL) -#undef memcpy -#undef memset -#undef memmove - void efi_char16_printk(efi_system_table_t *, efi_char16_t *); efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg, void *__image, diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c new file mode 100644 index 000000000000..bb6555f6d63b --- /dev/null +++ b/drivers/firmware/qcom_scm-64.c @@ -0,0 +1,63 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + */ + +#include <linux/io.h> +#include <linux/errno.h> +#include <linux/qcom_scm.h> + +/** + * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus + * @entry: Entry point function for the cpus + * @cpus: The cpumask of cpus that will use the entry point + * + * Set the cold boot address of the cpus. Any cpu outside the supported + * range would be removed from the cpu present mask. + */ +int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) +{ + return -ENOTSUPP; +} + +/** + * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus + * @entry: Entry point function for the cpus + * @cpus: The cpumask of cpus that will use the entry point + * + * Set the Linux entry point for the SCM to transfer control to when coming + * out of a power down. CPU power down may be executed on cpuidle or hotplug. + */ +int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) +{ + return -ENOTSUPP; +} + +/** + * qcom_scm_cpu_power_down() - Power down the cpu + * @flags - Flags to flush cache + * + * This is an end point to power down cpu. If there was a pending interrupt, + * the control would return from this function, otherwise, the cpu jumps to the + * warm boot entry point set for this cpu upon reset. + */ +void __qcom_scm_cpu_power_down(u32 flags) +{ +} + +int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id) +{ + return -ENOTSUPP; +} + +int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) +{ + return -ENOTSUPP; +} diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b4fc9e4d24c6..8949b3f6f74d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -356,7 +356,7 @@ config GPIO_PXA config GPIO_RCAR tristate "Renesas R-Car GPIO" - depends on ARM && (ARCH_SHMOBILE || COMPILE_TEST) + depends on ARCH_SHMOBILE || COMPILE_TEST select GPIOLIB_IRQCHIP help Say yes here to support GPIO on Renesas R-Car SoCs. diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 9b7e0b3db387..1b44941574fa 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -201,8 +201,7 @@ static int altera_gpio_direction_output(struct gpio_chip *gc, return 0; } -static void altera_gpio_irq_edge_handler(unsigned int irq, - struct irq_desc *desc) +static void altera_gpio_irq_edge_handler(struct irq_desc *desc) { struct altera_gpio_chip *altera_gc; struct irq_chip *chip; @@ -231,8 +230,7 @@ static void altera_gpio_irq_edge_handler(unsigned int irq, } -static void altera_gpio_irq_leveL_high_handler(unsigned int irq, - struct irq_desc *desc) +static void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc) { struct altera_gpio_chip *altera_gc; struct irq_chip *chip; diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 31b90ac15204..33a1f9779b86 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -433,7 +433,7 @@ static int bcm_kona_gpio_irq_set_type(struct irq_data *d, unsigned int type) return 0; } -static void bcm_kona_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +static void bcm_kona_gpio_irq_handler(struct irq_desc *desc) { void __iomem *reg_base; int bit, bank_id; diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 9ea86d2ac054..4c64627c6bb5 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -236,7 +236,7 @@ static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank) } /* Each UPG GIO block has one IRQ for all banks */ -static void brcmstb_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +static void brcmstb_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc); diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 94b0ab709721..5e715388803d 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -326,8 +326,7 @@ static struct irq_chip gpio_irqchip = { .flags = IRQCHIP_SET_TYPE_MASKED, }; -static void -gpio_irq_handler(unsigned __irq, struct irq_desc *desc) +static void gpio_irq_handler(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct davinci_gpio_regs __iomem *g; diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index c5be4b9b8baf..fcd5b0acfc72 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -147,7 +147,7 @@ static u32 dwapb_do_irq(struct dwapb_gpio *gpio) return ret; } -static void dwapb_irq_handler(u32 irq, struct irq_desc *desc) +static void dwapb_irq_handler(struct irq_desc *desc) { struct dwapb_gpio *gpio = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 9d90366ea259..3e3947b35c83 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -78,7 +78,7 @@ static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable) EP93XX_GPIO_REG(int_debounce_register_offset[port])); } -static void ep93xx_gpio_ab_irq_handler(unsigned int irq, struct irq_desc *desc) +static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc) { unsigned char status; int i; @@ -100,8 +100,7 @@ static void ep93xx_gpio_ab_irq_handler(unsigned int irq, struct irq_desc *desc) } } -static void ep93xx_gpio_f_irq_handler(unsigned int __irq, - struct irq_desc *desc) +static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc) { /* * map discontiguous hw irq range to continuous sw irq range: diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c index aa28c65eb6b4..70097472b02c 100644 --- a/drivers/gpio/gpio-intel-mid.c +++ b/drivers/gpio/gpio-intel-mid.c @@ -301,7 +301,7 @@ static const struct pci_device_id intel_gpio_ids[] = { }; MODULE_DEVICE_TABLE(pci, intel_gpio_ids); -static void intel_mid_irq_handler(unsigned irq, struct irq_desc *desc) +static void intel_mid_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct intel_mid_gpio *priv = to_intel_gpio_priv(gc); diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index 153af464c7a7..127c37b380ae 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -234,7 +234,7 @@ static int lp_gpio_direction_output(struct gpio_chip *chip, return 0; } -static void lp_gpio_irq_handler(unsigned hwirq, struct irq_desc *desc) +static void lp_gpio_irq_handler(struct irq_desc *desc) { struct irq_data *data = irq_desc_get_irq_data(desc); struct gpio_chip *gc = irq_desc_get_handler_data(desc); diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 8ef7a12de983..48ef368347ab 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -194,7 +194,7 @@ static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) return -ENXIO; } -static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc) +static void mpc8xxx_gpio_irq_cascade(struct irq_desc *desc) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c index 7bcfb87a5fa6..22523aae8abe 100644 --- a/drivers/gpio/gpio-msic.c +++ b/drivers/gpio/gpio-msic.c @@ -232,7 +232,7 @@ static struct irq_chip msic_irqchip = { .irq_bus_sync_unlock = msic_bus_sync_unlock, }; -static void msic_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +static void msic_gpio_irq_handler(struct irq_desc *desc) { struct irq_data *data = irq_desc_get_irq_data(desc); struct msic_gpio *mg = irq_data_get_irq_handler_data(data); diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c index d2012cfb5571..4b4222145f10 100644 --- a/drivers/gpio/gpio-msm-v2.c +++ b/drivers/gpio/gpio-msm-v2.c @@ -305,7 +305,7 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type) * which have been set as summary IRQ lines and which are triggered, * and to call their interrupt handlers. */ -static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc) +static void msm_summary_irq_handler(struct irq_desc *desc) { unsigned long i; struct irq_chip *chip = irq_desc_get_chip(desc); diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index b396bf3bf294..df418b81456d 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -458,7 +458,7 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) return 0; } -static void mvebu_gpio_irq_handler(unsigned int __irq, struct irq_desc *desc) +static void mvebu_gpio_irq_handler(struct irq_desc *desc) { struct mvebu_gpio_chip *mvchip = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index b752b560126e..b8dd847443c5 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -272,7 +272,7 @@ static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat) } /* MX1 and MX3 has one interrupt *per* gpio port */ -static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc) +static void mx3_gpio_irq_handler(struct irq_desc *desc) { u32 irq_stat; struct mxc_gpio_port *port = irq_desc_get_handler_data(desc); @@ -288,7 +288,7 @@ static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc) } /* MX2 has one interrupt *for all* gpio ports */ -static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc) +static void mx2_gpio_irq_handler(struct irq_desc *desc) { u32 irq_msk, irq_stat; struct mxc_gpio_port *port; @@ -339,13 +339,15 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable) return 0; } -static void mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base) +static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base) { struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_alloc_generic_chip("gpio-mxc", 1, irq_base, port->base, handle_level_irq); + if (!gc) + return -ENOMEM; gc->private = port; ct = gc->chip_types; @@ -360,6 +362,8 @@ static void mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base) irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0); + + return 0; } static void mxc_gpio_get_hw(struct platform_device *pdev) @@ -477,12 +481,16 @@ static int mxc_gpio_probe(struct platform_device *pdev) } /* gpio-mxc can be a generic irq chip */ - mxc_gpio_init_gc(port, irq_base); + err = mxc_gpio_init_gc(port, irq_base); + if (err < 0) + goto out_irqdomain_remove; list_add_tail(&port->node, &mxc_gpio_ports); return 0; +out_irqdomain_remove: + irq_domain_remove(port->domain); out_irqdesc_free: irq_free_descs(irq_base, 32); out_gpiochip_remove: diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index b7f383eb18d9..a4288f428819 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -154,7 +154,7 @@ static void mxs_flip_edge(struct mxs_gpio_port *port, u32 gpio) } /* MXS has one interrupt *per* gpio port */ -static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) +static void mxs_gpio_irq_handler(struct irq_desc *desc) { u32 irq_stat; struct mxs_gpio_port *port = irq_desc_get_handler_data(desc); @@ -196,13 +196,16 @@ static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable) return 0; } -static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base) +static int __init mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base) { struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_alloc_generic_chip("gpio-mxs", 1, irq_base, port->base, handle_level_irq); + if (!gc) + return -ENOMEM; + gc->private = port; ct = gc->chip_types; @@ -216,6 +219,8 @@ static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base) irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0); + + return 0; } static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset) @@ -317,7 +322,9 @@ static int mxs_gpio_probe(struct platform_device *pdev) } /* gpio-mxs can be a generic irq chip */ - mxs_gpio_init_gc(port, irq_base); + err = mxs_gpio_init_gc(port, irq_base); + if (err < 0) + goto out_irqdomain_remove; /* setup one handler for each entry */ irq_set_chained_handler_and_data(port->irq, mxs_gpio_irq_handler, @@ -343,6 +350,8 @@ static int mxs_gpio_probe(struct platform_device *pdev) out_bgpio_remove: bgpio_remove(&port->bgc); +out_irqdomain_remove: + irq_domain_remove(port->domain); out_irqdesc_free: irq_free_descs(irq_base, 32); return err; diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 2ae0d47e9554..5236db161e76 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -709,7 +709,7 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset) * line's interrupt handler has been run, we may miss some nested * interrupts. */ -static void omap_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +static void omap_gpio_irq_handler(struct irq_desc *desc) { void __iomem *isr_reg = NULL; u32 isr; @@ -1098,7 +1098,6 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) } else { bank->chip.label = "gpio"; bank->chip.base = gpio; - gpio += bank->width; } bank->chip.ngpio = bank->width; @@ -1108,6 +1107,9 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) return ret; } + if (!bank->is_mpuio) + gpio += bank->width; + #ifdef CONFIG_ARCH_OMAP1 /* * REVISIT: Once we have OMAP1 supporting SPARSE_IRQ, we can drop @@ -1253,8 +1255,11 @@ static int omap_gpio_probe(struct platform_device *pdev) omap_gpio_mod_init(bank); ret = omap_gpio_chip_init(bank, irqc); - if (ret) + if (ret) { + pm_runtime_put_sync(bank->dev); + pm_runtime_disable(bank->dev); return ret; + } omap_gpio_show_rev(bank); diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 04756130437f..229ef653e0f8 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -187,7 +187,7 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger) return 0; } -static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) +static void pl061_irq_handler(struct irq_desc *desc) { unsigned long pending; int offset; diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 55a11de3d5b7..df2ce550f309 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -401,7 +401,7 @@ static int pxa_gpio_irq_type(struct irq_data *d, unsigned int type) return 0; } -static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc) +static void pxa_gpio_demux_handler(struct irq_desc *desc) { struct pxa_gpio_chip *c; int loop, gpio, gpio_base, n; diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c index 67bd2f5d89e8..990fa9023e22 100644 --- a/drivers/gpio/gpio-sa1100.c +++ b/drivers/gpio/gpio-sa1100.c @@ -172,8 +172,7 @@ static struct irq_domain *sa1100_gpio_irqdomain; * irq_controller_lock held, and IRQs disabled. Decode the IRQ * and call the handler. */ -static void -sa1100_gpio_handler(unsigned int __irq, struct irq_desc *desc) +static void sa1100_gpio_handler(struct irq_desc *desc) { unsigned int irq, mask; diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c index 458d9d7952b8..9c6b96707c9f 100644 --- a/drivers/gpio/gpio-sx150x.c +++ b/drivers/gpio/gpio-sx150x.c @@ -706,4 +706,3 @@ module_exit(sx150x_exit); MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>"); MODULE_DESCRIPTION("Driver for Semtech SX150X I2C GPIO Expanders"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("i2c:sx150x"); diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 9b14aafb576d..027e5f47dd28 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -266,7 +266,7 @@ static void tegra_gpio_irq_shutdown(struct irq_data *d) gpiochip_unlock_as_irq(&tegra_gpio_chip, gpio); } -static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +static void tegra_gpio_irq_handler(struct irq_desc *desc) { int port; int pin; diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index 5a492054589f..30653e6319e9 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -192,7 +192,7 @@ out: return ret; } -static void timbgpio_irq(unsigned int irq, struct irq_desc *desc) +static void timbgpio_irq(struct irq_desc *desc) { struct timbgpio *tgpio = irq_desc_get_handler_data(desc); struct irq_data *data = irq_desc_get_irq_data(desc); diff --git a/drivers/gpio/gpio-tz1090.c b/drivers/gpio/gpio-tz1090.c index bbac92ae4c32..87bb1b1eee8d 100644 --- a/drivers/gpio/gpio-tz1090.c +++ b/drivers/gpio/gpio-tz1090.c @@ -375,7 +375,7 @@ static int gpio_set_irq_wake(struct irq_data *data, unsigned int on) #define gpio_set_irq_wake NULL #endif -static void tz1090_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +static void tz1090_gpio_irq_handler(struct irq_desc *desc) { irq_hw_number_t hw; unsigned int irq_stat, irq_no; @@ -400,7 +400,7 @@ static void tz1090_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) == IRQ_TYPE_EDGE_BOTH) tz1090_gpio_irq_next_edge(bank, hw); - generic_handle_irq_desc(irq_no, child_desc); + generic_handle_irq_desc(child_desc); } } diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 3d5714d4f405..069f9e4b7daa 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -120,7 +120,7 @@ static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, return pinctrl_gpio_direction_output(chip->base + gpio); } -static void vf610_gpio_irq_handler(u32 irq, struct irq_desc *desc) +static void vf610_gpio_irq_handler(struct irq_desc *desc) { struct vf610_gpio_port *port = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); @@ -176,9 +176,9 @@ static int vf610_gpio_irq_set_type(struct irq_data *d, u32 type) port->irqc[d->hwirq] = irqc; if (type & IRQ_TYPE_LEVEL_MASK) - __irq_set_handler_locked(d->irq, handle_level_irq); + irq_set_handler_locked(d, handle_level_irq); else - __irq_set_handler_locked(d->irq, handle_edge_irq); + irq_set_handler_locked(d, handle_edge_irq); return 0; } diff --git a/drivers/gpio/gpio-zx.c b/drivers/gpio/gpio-zx.c index 12ee1969298c..4b8a26910705 100644 --- a/drivers/gpio/gpio-zx.c +++ b/drivers/gpio/gpio-zx.c @@ -177,7 +177,7 @@ static int zx_irq_type(struct irq_data *d, unsigned trigger) return 0; } -static void zx_irq_handler(unsigned irq, struct irq_desc *desc) +static void zx_irq_handler(struct irq_desc *desc) { unsigned long pending; int offset; diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 27348e7cb705..1d1a5865ede9 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -514,7 +514,7 @@ static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio, * application for that pin. * Note: A bug is reported if no handler is set for the gpio pin. */ -static void zynq_gpio_irqhandler(unsigned int irq, struct irq_desc *desc) +static void zynq_gpio_irqhandler(struct irq_desc *desc) { u32 int_sts, int_enb; unsigned int bank_num; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 980c1f87866a..5db3445552b1 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1174,15 +1174,16 @@ EXPORT_SYMBOL_GPL(gpiod_is_active_low); * that the GPIO was actually requested. */ -static bool _gpiod_get_raw_value(const struct gpio_desc *desc) +static int _gpiod_get_raw_value(const struct gpio_desc *desc) { struct gpio_chip *chip; - bool value; int offset; + int value; chip = desc->chip; offset = gpio_chip_hwgpio(desc); - value = chip->get ? chip->get(chip, offset) : false; + value = chip->get ? chip->get(chip, offset) : -EIO; + value = value < 0 ? value : !!value; trace_gpio_value(desc_to_gpio(desc), 1, value); return value; } @@ -1192,7 +1193,7 @@ static bool _gpiod_get_raw_value(const struct gpio_desc *desc) * @desc: gpio whose value will be returned * * Return the GPIO's raw value, i.e. the value of the physical line disregarding - * its ACTIVE_LOW status. + * its ACTIVE_LOW status, or negative errno on failure. * * This function should be called from contexts where we cannot sleep, and will * complain if the GPIO chip functions potentially sleep. @@ -1212,7 +1213,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_value); * @desc: gpio whose value will be returned * * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into - * account. + * account, or negative errno on failure. * * This function should be called from contexts where we cannot sleep, and will * complain if the GPIO chip functions potentially sleep. @@ -1226,6 +1227,9 @@ int gpiod_get_value(const struct gpio_desc *desc) WARN_ON(desc->chip->can_sleep); value = _gpiod_get_raw_value(desc); + if (value < 0) + return value; + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; @@ -1548,7 +1552,7 @@ EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq); * @desc: gpio whose value will be returned * * Return the GPIO's raw value, i.e. the value of the physical line disregarding - * its ACTIVE_LOW status. + * its ACTIVE_LOW status, or negative errno on failure. * * This function is to be called from contexts that can sleep. */ @@ -1566,7 +1570,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_value_cansleep); * @desc: gpio whose value will be returned * * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into - * account. + * account, or negative errno on failure. * * This function is to be called from contexts that can sleep. */ @@ -1579,6 +1583,9 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) return 0; value = _gpiod_get_raw_value(desc); + if (value < 0) + return value; + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 668939a14206..6647fb26ef25 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -82,6 +82,7 @@ extern int amdgpu_vm_block_size; extern int amdgpu_enable_scheduler; extern int amdgpu_sched_jobs; extern int amdgpu_sched_hw_submission; +extern int amdgpu_enable_semaphores; #define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS 3000 #define AMDGPU_MAX_USEC_TIMEOUT 100000 /* 100 ms */ @@ -432,7 +433,7 @@ int amdgpu_fence_driver_init(struct amdgpu_device *adev); void amdgpu_fence_driver_fini(struct amdgpu_device *adev); void amdgpu_fence_driver_force_completion(struct amdgpu_device *adev); -void amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring); +int amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring); int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring, struct amdgpu_irq_src *irq_src, unsigned irq_type); @@ -890,7 +891,7 @@ struct amdgpu_ring { struct amdgpu_device *adev; const struct amdgpu_ring_funcs *funcs; struct amdgpu_fence_driver fence_drv; - struct amd_gpu_scheduler *scheduler; + struct amd_gpu_scheduler sched; spinlock_t fence_lock; struct mutex *ring_lock; @@ -1201,8 +1202,6 @@ struct amdgpu_gfx { struct amdgpu_irq_src priv_inst_irq; /* gfx status */ uint32_t gfx_current_status; - /* sync signal for const engine */ - unsigned ce_sync_offs; /* ce ram size*/ unsigned ce_ram_size; }; @@ -1274,8 +1273,10 @@ struct amdgpu_job { uint32_t num_ibs; struct mutex job_lock; struct amdgpu_user_fence uf; - int (*free_job)(struct amdgpu_job *sched_job); + int (*free_job)(struct amdgpu_job *job); }; +#define to_amdgpu_job(sched_job) \ + container_of((sched_job), struct amdgpu_job, base) static inline u32 amdgpu_get_ib_value(struct amdgpu_cs_parser *p, uint32_t ib_idx, int idx) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 496ed2192eba..84d68d658f8a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -183,7 +183,7 @@ int alloc_gtt_mem(struct kgd_dev *kgd, size_t size, return -ENOMEM; r = amdgpu_bo_create(rdev, size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_GTT, - AMDGPU_GEM_CREATE_CPU_GTT_USWC, NULL, &(*mem)->bo); + AMDGPU_GEM_CREATE_CPU_GTT_USWC, NULL, NULL, &(*mem)->bo); if (r) { dev_err(rdev->dev, "failed to allocate BO for amdkfd (%d)\n", r); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c index 98d59ee640ce..cd639c362df3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c @@ -79,7 +79,8 @@ static void amdgpu_benchmark_move(struct amdgpu_device *adev, unsigned size, int time; n = AMDGPU_BENCHMARK_ITERATIONS; - r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, sdomain, 0, NULL, &sobj); + r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, sdomain, 0, NULL, + NULL, &sobj); if (r) { goto out_cleanup; } @@ -91,7 +92,8 @@ static void amdgpu_benchmark_move(struct amdgpu_device *adev, unsigned size, if (r) { goto out_cleanup; } - r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, ddomain, 0, NULL, &dobj); + r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, ddomain, 0, NULL, + NULL, &dobj); if (r) { goto out_cleanup; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c index 6b1243f9f86d..1c3fc99c5465 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c @@ -86,7 +86,7 @@ static int amdgpu_cgs_gmap_kmem(void *cgs_device, void *kmem, struct sg_table *sg = drm_prime_pages_to_sg(&kmem_page, npages); ret = amdgpu_bo_create(adev, size, PAGE_SIZE, false, - AMDGPU_GEM_DOMAIN_GTT, 0, sg, &bo); + AMDGPU_GEM_DOMAIN_GTT, 0, sg, NULL, &bo); if (ret) return ret; ret = amdgpu_bo_reserve(bo, false); @@ -197,7 +197,8 @@ static int amdgpu_cgs_alloc_gpu_mem(void *cgs_device, ret = amdgpu_bo_create_restricted(adev, size, PAGE_SIZE, true, domain, flags, - NULL, &placement, &obj); + NULL, &placement, NULL, + &obj); if (ret) { DRM_ERROR("(%d) bo create failed\n", ret); return ret; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 3b355aeb62fd..749420f1ea6f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -154,42 +154,41 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data) { union drm_amdgpu_cs *cs = data; uint64_t *chunk_array_user; - uint64_t *chunk_array = NULL; + uint64_t *chunk_array; struct amdgpu_fpriv *fpriv = p->filp->driver_priv; unsigned size, i; - int r = 0; + int ret; - if (!cs->in.num_chunks) - goto out; + if (cs->in.num_chunks == 0) + return 0; + + chunk_array = kmalloc_array(cs->in.num_chunks, sizeof(uint64_t), GFP_KERNEL); + if (!chunk_array) + return -ENOMEM; p->ctx = amdgpu_ctx_get(fpriv, cs->in.ctx_id); if (!p->ctx) { - r = -EINVAL; - goto out; + ret = -EINVAL; + goto free_chunk; } + p->bo_list = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle); /* get chunks */ INIT_LIST_HEAD(&p->validated); - chunk_array = kmalloc_array(cs->in.num_chunks, sizeof(uint64_t), GFP_KERNEL); - if (chunk_array == NULL) { - r = -ENOMEM; - goto out; - } - chunk_array_user = (uint64_t __user *)(cs->in.chunks); if (copy_from_user(chunk_array, chunk_array_user, sizeof(uint64_t)*cs->in.num_chunks)) { - r = -EFAULT; - goto out; + ret = -EFAULT; + goto put_bo_list; } p->nchunks = cs->in.num_chunks; p->chunks = kmalloc_array(p->nchunks, sizeof(struct amdgpu_cs_chunk), GFP_KERNEL); - if (p->chunks == NULL) { - r = -ENOMEM; - goto out; + if (!p->chunks) { + ret = -ENOMEM; + goto put_bo_list; } for (i = 0; i < p->nchunks; i++) { @@ -200,8 +199,9 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data) chunk_ptr = (void __user *)chunk_array[i]; if (copy_from_user(&user_chunk, chunk_ptr, sizeof(struct drm_amdgpu_cs_chunk))) { - r = -EFAULT; - goto out; + ret = -EFAULT; + i--; + goto free_partial_kdata; } p->chunks[i].chunk_id = user_chunk.chunk_id; p->chunks[i].length_dw = user_chunk.length_dw; @@ -212,13 +212,14 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data) p->chunks[i].kdata = drm_malloc_ab(size, sizeof(uint32_t)); if (p->chunks[i].kdata == NULL) { - r = -ENOMEM; - goto out; + ret = -ENOMEM; + i--; + goto free_partial_kdata; } size *= sizeof(uint32_t); if (copy_from_user(p->chunks[i].kdata, cdata, size)) { - r = -EFAULT; - goto out; + ret = -EFAULT; + goto free_partial_kdata; } switch (p->chunks[i].chunk_id) { @@ -238,15 +239,15 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data) gobj = drm_gem_object_lookup(p->adev->ddev, p->filp, handle); if (gobj == NULL) { - r = -EINVAL; - goto out; + ret = -EINVAL; + goto free_partial_kdata; } p->uf.bo = gem_to_amdgpu_bo(gobj); p->uf.offset = fence_data->offset; } else { - r = -EINVAL; - goto out; + ret = -EINVAL; + goto free_partial_kdata; } break; @@ -254,19 +255,35 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data) break; default: - r = -EINVAL; - goto out; + ret = -EINVAL; + goto free_partial_kdata; } } p->ibs = kcalloc(p->num_ibs, sizeof(struct amdgpu_ib), GFP_KERNEL); - if (!p->ibs) - r = -ENOMEM; + if (!p->ibs) { + ret = -ENOMEM; + goto free_all_kdata; + } -out: kfree(chunk_array); - return r; + return 0; + +free_all_kdata: + i = p->nchunks - 1; +free_partial_kdata: + for (; i >= 0; i--) + drm_free_large(p->chunks[i].kdata); + kfree(p->chunks); +put_bo_list: + if (p->bo_list) + amdgpu_bo_list_put(p->bo_list); + amdgpu_ctx_put(p->ctx); +free_chunk: + kfree(chunk_array); + + return ret; } /* Returns how many bytes TTM can move per IB. @@ -321,25 +338,17 @@ static u64 amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev) return max(bytes_moved_threshold, 1024*1024ull); } -int amdgpu_cs_list_validate(struct amdgpu_cs_parser *p) +int amdgpu_cs_list_validate(struct amdgpu_device *adev, + struct amdgpu_vm *vm, + struct list_head *validated) { - struct amdgpu_fpriv *fpriv = p->filp->driver_priv; - struct amdgpu_vm *vm = &fpriv->vm; - struct amdgpu_device *adev = p->adev; struct amdgpu_bo_list_entry *lobj; - struct list_head duplicates; struct amdgpu_bo *bo; u64 bytes_moved = 0, initial_bytes_moved; u64 bytes_moved_threshold = amdgpu_cs_get_threshold_for_moves(adev); int r; - INIT_LIST_HEAD(&duplicates); - r = ttm_eu_reserve_buffers(&p->ticket, &p->validated, true, &duplicates); - if (unlikely(r != 0)) { - return r; - } - - list_for_each_entry(lobj, &p->validated, tv.head) { + list_for_each_entry(lobj, validated, tv.head) { bo = lobj->robj; if (!bo->pin_count) { u32 domain = lobj->prefered_domains; @@ -373,7 +382,6 @@ int amdgpu_cs_list_validate(struct amdgpu_cs_parser *p) domain = lobj->allowed_domains; goto retry; } - ttm_eu_backoff_reservation(&p->ticket, &p->validated); return r; } } @@ -386,6 +394,7 @@ static int amdgpu_cs_parser_relocs(struct amdgpu_cs_parser *p) { struct amdgpu_fpriv *fpriv = p->filp->driver_priv; struct amdgpu_cs_buckets buckets; + struct list_head duplicates; bool need_mmap_lock = false; int i, r; @@ -405,8 +414,22 @@ static int amdgpu_cs_parser_relocs(struct amdgpu_cs_parser *p) if (need_mmap_lock) down_read(¤t->mm->mmap_sem); - r = amdgpu_cs_list_validate(p); + INIT_LIST_HEAD(&duplicates); + r = ttm_eu_reserve_buffers(&p->ticket, &p->validated, true, &duplicates); + if (unlikely(r != 0)) + goto error_reserve; + + r = amdgpu_cs_list_validate(p->adev, &fpriv->vm, &p->validated); + if (r) + goto error_validate; + + r = amdgpu_cs_list_validate(p->adev, &fpriv->vm, &duplicates); + +error_validate: + if (r) + ttm_eu_backoff_reservation(&p->ticket, &p->validated); +error_reserve: if (need_mmap_lock) up_read(¤t->mm->mmap_sem); @@ -772,15 +795,15 @@ static int amdgpu_cs_dependencies(struct amdgpu_device *adev, return 0; } -static int amdgpu_cs_free_job(struct amdgpu_job *sched_job) +static int amdgpu_cs_free_job(struct amdgpu_job *job) { int i; - if (sched_job->ibs) - for (i = 0; i < sched_job->num_ibs; i++) - amdgpu_ib_free(sched_job->adev, &sched_job->ibs[i]); - kfree(sched_job->ibs); - if (sched_job->uf.bo) - drm_gem_object_unreference_unlocked(&sched_job->uf.bo->gem_base); + if (job->ibs) + for (i = 0; i < job->num_ibs; i++) + amdgpu_ib_free(job->adev, &job->ibs[i]); + kfree(job->ibs); + if (job->uf.bo) + drm_gem_object_unreference_unlocked(&job->uf.bo->gem_base); return 0; } @@ -804,7 +827,7 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) r = amdgpu_cs_parser_init(parser, data); if (r) { DRM_ERROR("Failed to initialize parser !\n"); - amdgpu_cs_parser_fini(parser, r, false); + kfree(parser); up_read(&adev->exclusive_lock); r = amdgpu_cs_handle_lockup(adev, r); return r; @@ -842,7 +865,7 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) job = kzalloc(sizeof(struct amdgpu_job), GFP_KERNEL); if (!job) return -ENOMEM; - job->base.sched = ring->scheduler; + job->base.sched = &ring->sched; job->base.s_entity = &parser->ctx->rings[ring->idx].entity; job->adev = parser->adev; job->ibs = parser->ibs; @@ -857,7 +880,7 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) job->free_job = amdgpu_cs_free_job; mutex_lock(&job->job_lock); - r = amd_sched_entity_push_job((struct amd_sched_job *)job); + r = amd_sched_entity_push_job(&job->base); if (r) { mutex_unlock(&job->job_lock); amdgpu_cs_free_job(job); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index 20cbc4eb5a6f..e0b80ccdfe8a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -43,10 +43,10 @@ int amdgpu_ctx_init(struct amdgpu_device *adev, bool kernel, for (i = 0; i < adev->num_rings; i++) { struct amd_sched_rq *rq; if (kernel) - rq = &adev->rings[i]->scheduler->kernel_rq; + rq = &adev->rings[i]->sched.kernel_rq; else - rq = &adev->rings[i]->scheduler->sched_rq; - r = amd_sched_entity_init(adev->rings[i]->scheduler, + rq = &adev->rings[i]->sched.sched_rq; + r = amd_sched_entity_init(&adev->rings[i]->sched, &ctx->rings[i].entity, rq, amdgpu_sched_jobs); if (r) @@ -55,7 +55,7 @@ int amdgpu_ctx_init(struct amdgpu_device *adev, bool kernel, if (i < adev->num_rings) { for (j = 0; j < i; j++) - amd_sched_entity_fini(adev->rings[j]->scheduler, + amd_sched_entity_fini(&adev->rings[j]->sched, &ctx->rings[j].entity); kfree(ctx); return r; @@ -75,7 +75,7 @@ void amdgpu_ctx_fini(struct amdgpu_ctx *ctx) if (amdgpu_enable_scheduler) { for (i = 0; i < adev->num_rings; i++) - amd_sched_entity_fini(adev->rings[i]->scheduler, + amd_sched_entity_fini(&adev->rings[i]->sched, &ctx->rings[i].entity); } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 6ff6ae945794..6068d8207d10 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -246,7 +246,7 @@ static int amdgpu_vram_scratch_init(struct amdgpu_device *adev) r = amdgpu_bo_create(adev, AMDGPU_GPU_PAGE_SIZE, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, &adev->vram_scratch.robj); + NULL, NULL, &adev->vram_scratch.robj); if (r) { return r; } @@ -449,7 +449,8 @@ static int amdgpu_wb_init(struct amdgpu_device *adev) if (adev->wb.wb_obj == NULL) { r = amdgpu_bo_create(adev, AMDGPU_MAX_WB * 4, PAGE_SIZE, true, - AMDGPU_GEM_DOMAIN_GTT, 0, NULL, &adev->wb.wb_obj); + AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, + &adev->wb.wb_obj); if (r) { dev_warn(adev->dev, "(%d) create WB bo failed\n", r); return r; @@ -1650,9 +1651,11 @@ int amdgpu_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon) drm_kms_helper_poll_disable(dev); /* turn off display hw */ + drm_modeset_lock_all(dev); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); } + drm_modeset_unlock_all(dev); /* unpin the front buffers */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -1747,9 +1750,11 @@ int amdgpu_resume_kms(struct drm_device *dev, bool resume, bool fbcon) if (fbcon) { drm_helper_resume_force_mode(dev); /* turn on display hw */ + drm_modeset_lock_all(dev); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); } + drm_modeset_unlock_all(dev); } drm_kms_helper_poll_enable(dev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 0fcc0bd1622c..adb48353f2e1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -79,6 +79,7 @@ int amdgpu_exp_hw_support = 0; int amdgpu_enable_scheduler = 0; int amdgpu_sched_jobs = 16; int amdgpu_sched_hw_submission = 2; +int amdgpu_enable_semaphores = 1; MODULE_PARM_DESC(vramlimit, "Restrict VRAM for testing, in megabytes"); module_param_named(vramlimit, amdgpu_vram_limit, int, 0600); @@ -152,6 +153,9 @@ module_param_named(sched_jobs, amdgpu_sched_jobs, int, 0444); MODULE_PARM_DESC(sched_hw_submission, "the max number of HW submissions (default 2)"); module_param_named(sched_hw_submission, amdgpu_sched_hw_submission, int, 0444); +MODULE_PARM_DESC(enable_semaphores, "Enable semaphores (1 = enable (default), 0 = disable)"); +module_param_named(enable_semaphores, amdgpu_enable_semaphores, int, 0644); + static struct pci_device_id pciidlist[] = { #ifdef CONFIG_DRM_AMDGPU_CIK /* Kaveri */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c index 1be2bd6d07ea..b3fc26c59787 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c @@ -609,9 +609,9 @@ int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring, * Init the fence driver for the requested ring (all asics). * Helper function for amdgpu_fence_driver_init(). */ -void amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring) +int amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring) { - int i; + int i, r; ring->fence_drv.cpu_addr = NULL; ring->fence_drv.gpu_addr = 0; @@ -625,15 +625,19 @@ void amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring) amdgpu_fence_check_lockup); ring->fence_drv.ring = ring; + init_waitqueue_head(&ring->fence_drv.fence_queue); + if (amdgpu_enable_scheduler) { - ring->scheduler = amd_sched_create(&amdgpu_sched_ops, - ring->idx, - amdgpu_sched_hw_submission, - (void *)ring->adev); - if (!ring->scheduler) - DRM_ERROR("Failed to create scheduler on ring %d.\n", - ring->idx); + r = amd_sched_init(&ring->sched, &amdgpu_sched_ops, + amdgpu_sched_hw_submission, ring->name); + if (r) { + DRM_ERROR("Failed to create scheduler on ring %s.\n", + ring->name); + return r; + } } + + return 0; } /** @@ -681,8 +685,7 @@ void amdgpu_fence_driver_fini(struct amdgpu_device *adev) wake_up_all(&ring->fence_drv.fence_queue); amdgpu_irq_put(adev, ring->fence_drv.irq_src, ring->fence_drv.irq_type); - if (ring->scheduler) - amd_sched_destroy(ring->scheduler); + amd_sched_fini(&ring->sched); ring->fence_drv.initialized = false; } mutex_unlock(&adev->ring_lock); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c index cbd3a486c5c2..7312d729d300 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c @@ -127,7 +127,7 @@ int amdgpu_gart_table_vram_alloc(struct amdgpu_device *adev) r = amdgpu_bo_create(adev, adev->gart.table_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, &adev->gart.robj); + NULL, NULL, &adev->gart.robj); if (r) { return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 5839fab374bf..7297ca3a0ba7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -69,7 +69,8 @@ int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size, } } retry: - r = amdgpu_bo_create(adev, size, alignment, kernel, initial_domain, flags, NULL, &robj); + r = amdgpu_bo_create(adev, size, alignment, kernel, initial_domain, + flags, NULL, NULL, &robj); if (r) { if (r != -ERESTARTSYS) { if (initial_domain == AMDGPU_GEM_DOMAIN_VRAM) { @@ -426,6 +427,10 @@ int amdgpu_gem_metadata_ioctl(struct drm_device *dev, void *data, &args->data.data_size_bytes, &args->data.flags); } else if (args->op == AMDGPU_GEM_METADATA_OP_SET_METADATA) { + if (args->data.data_size_bytes > sizeof(args->data.data)) { + r = -EINVAL; + goto unreserve; + } r = amdgpu_bo_set_tiling_flags(robj, args->data.tiling_info); if (!r) r = amdgpu_bo_set_metadata(robj, args->data.data, @@ -433,6 +438,7 @@ int amdgpu_gem_metadata_ioctl(struct drm_device *dev, void *data, args->data.flags); } +unreserve: amdgpu_bo_unreserve(robj); out: drm_gem_object_unreference_unlocked(gobj); @@ -454,11 +460,12 @@ static void amdgpu_gem_va_update_vm(struct amdgpu_device *adev, struct ttm_validate_buffer tv, *entry; struct amdgpu_bo_list_entry *vm_bos; struct ww_acquire_ctx ticket; - struct list_head list; + struct list_head list, duplicates; unsigned domain; int r; INIT_LIST_HEAD(&list); + INIT_LIST_HEAD(&duplicates); tv.bo = &bo_va->bo->tbo; tv.shared = true; @@ -468,7 +475,8 @@ static void amdgpu_gem_va_update_vm(struct amdgpu_device *adev, if (!vm_bos) return; - r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL); + /* Provide duplicates to avoid -EALREADY */ + r = ttm_eu_reserve_buffers(&ticket, &list, true, &duplicates); if (r) goto error_free; @@ -651,7 +659,7 @@ int amdgpu_mode_dumb_create(struct drm_file *file_priv, int r; args->pitch = amdgpu_align_pitch(adev, args->width, args->bpp, 0) * ((args->bpp + 1) / 8); - args->size = args->pitch * args->height; + args->size = (u64)args->pitch * args->height; args->size = ALIGN(args->size, PAGE_SIZE); r = amdgpu_gem_object_create(adev, args->size, 0, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c index 5c8a803acedc..534fc04e80fd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c @@ -43,7 +43,7 @@ static int amdgpu_ih_ring_alloc(struct amdgpu_device *adev) r = amdgpu_bo_create(adev, adev->irq.ih.ring_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_GTT, 0, - NULL, &adev->irq.ih.ring_obj); + NULL, NULL, &adev->irq.ih.ring_obj); if (r) { DRM_ERROR("amdgpu: failed to create ih ring buffer (%d).\n", r); return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index 0aba8e9bc8a0..7c42ff670080 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c @@ -140,7 +140,7 @@ void amdgpu_irq_preinstall(struct drm_device *dev) */ int amdgpu_irq_postinstall(struct drm_device *dev) { - dev->max_vblank_count = 0x001fffff; + dev->max_vblank_count = 0x00ffffff; return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 22367939ebf1..8c735f544b66 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -390,7 +390,7 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file min((size_t)size, sizeof(vram_gtt))) ? -EFAULT : 0; } case AMDGPU_INFO_READ_MMR_REG: { - unsigned n, alloc_size = info->read_mmr_reg.count * 4; + unsigned n, alloc_size; uint32_t *regs; unsigned se_num = (info->read_mmr_reg.instance >> AMDGPU_INFO_MMR_SE_INDEX_SHIFT) & @@ -406,9 +406,10 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file if (sh_num == AMDGPU_INFO_MMR_SH_INDEX_MASK) sh_num = 0xffffffff; - regs = kmalloc(alloc_size, GFP_KERNEL); + regs = kmalloc_array(info->read_mmr_reg.count, sizeof(*regs), GFP_KERNEL); if (!regs) return -ENOMEM; + alloc_size = info->read_mmr_reg.count * sizeof(*regs); for (i = 0; i < info->read_mmr_reg.count; i++) if (amdgpu_asic_read_register(adev, se_num, sh_num, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 08b09d55b96f..1a7708f365f3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -215,6 +215,7 @@ int amdgpu_bo_create_restricted(struct amdgpu_device *adev, bool kernel, u32 domain, u64 flags, struct sg_table *sg, struct ttm_placement *placement, + struct reservation_object *resv, struct amdgpu_bo **bo_ptr) { struct amdgpu_bo *bo; @@ -261,7 +262,7 @@ int amdgpu_bo_create_restricted(struct amdgpu_device *adev, /* Kernel allocation are uninterruptible */ r = ttm_bo_init(&adev->mman.bdev, &bo->tbo, size, type, &bo->placement, page_align, !kernel, NULL, - acc_size, sg, NULL, &amdgpu_ttm_bo_destroy); + acc_size, sg, resv, &amdgpu_ttm_bo_destroy); if (unlikely(r != 0)) { return r; } @@ -275,7 +276,9 @@ int amdgpu_bo_create_restricted(struct amdgpu_device *adev, int amdgpu_bo_create(struct amdgpu_device *adev, unsigned long size, int byte_align, bool kernel, u32 domain, u64 flags, - struct sg_table *sg, struct amdgpu_bo **bo_ptr) + struct sg_table *sg, + struct reservation_object *resv, + struct amdgpu_bo **bo_ptr) { struct ttm_placement placement = {0}; struct ttm_place placements[AMDGPU_GEM_DOMAIN_MAX + 1]; @@ -286,11 +289,9 @@ int amdgpu_bo_create(struct amdgpu_device *adev, amdgpu_ttm_placement_init(adev, &placement, placements, domain, flags); - return amdgpu_bo_create_restricted(adev, size, byte_align, - kernel, domain, flags, - sg, - &placement, - bo_ptr); + return amdgpu_bo_create_restricted(adev, size, byte_align, kernel, + domain, flags, sg, &placement, + resv, bo_ptr); } int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr) @@ -535,12 +536,10 @@ int amdgpu_bo_set_metadata (struct amdgpu_bo *bo, void *metadata, if (metadata == NULL) return -EINVAL; - buffer = kzalloc(metadata_size, GFP_KERNEL); + buffer = kmemdup(metadata, metadata_size, GFP_KERNEL); if (buffer == NULL) return -ENOMEM; - memcpy(buffer, metadata, metadata_size); - kfree(bo->metadata); bo->metadata_flags = flags; bo->metadata = buffer; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h index 6ea18dcec561..3c2ff4567798 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h @@ -129,12 +129,14 @@ int amdgpu_bo_create(struct amdgpu_device *adev, unsigned long size, int byte_align, bool kernel, u32 domain, u64 flags, struct sg_table *sg, + struct reservation_object *resv, struct amdgpu_bo **bo_ptr); int amdgpu_bo_create_restricted(struct amdgpu_device *adev, unsigned long size, int byte_align, bool kernel, u32 domain, u64 flags, struct sg_table *sg, struct ttm_placement *placement, + struct reservation_object *resv, struct amdgpu_bo **bo_ptr); int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr); void amdgpu_bo_kunmap(struct amdgpu_bo *bo); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c index d9652fe32d6a..59f735a933a9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c @@ -61,12 +61,15 @@ struct drm_gem_object *amdgpu_gem_prime_import_sg_table(struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sg) { + struct reservation_object *resv = attach->dmabuf->resv; struct amdgpu_device *adev = dev->dev_private; struct amdgpu_bo *bo; int ret; + ww_mutex_lock(&resv->lock, NULL); ret = amdgpu_bo_create(adev, attach->dmabuf->size, PAGE_SIZE, false, - AMDGPU_GEM_DOMAIN_GTT, 0, sg, &bo); + AMDGPU_GEM_DOMAIN_GTT, 0, sg, resv, &bo); + ww_mutex_unlock(&resv->lock); if (ret) return ERR_PTR(ret); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c index 9bec91484c24..30dce235ddeb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c @@ -357,11 +357,11 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, ring->adev = adev; ring->idx = adev->num_rings++; adev->rings[ring->idx] = ring; - amdgpu_fence_driver_init_ring(ring); + r = amdgpu_fence_driver_init_ring(ring); + if (r) + return r; } - init_waitqueue_head(&ring->fence_drv.fence_queue); - r = amdgpu_wb_get(adev, &ring->rptr_offs); if (r) { dev_err(adev->dev, "(%d) ring rptr_offs wb alloc failed\n", r); @@ -407,7 +407,7 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, if (ring->ring_obj == NULL) { r = amdgpu_bo_create(adev, ring->ring_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_GTT, 0, - NULL, &ring->ring_obj); + NULL, NULL, &ring->ring_obj); if (r) { dev_err(adev->dev, "(%d) ring create failed\n", r); return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c index 74dad270362c..e90712443fe9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c @@ -64,8 +64,8 @@ int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev, INIT_LIST_HEAD(&sa_manager->flist[i]); } - r = amdgpu_bo_create(adev, size, align, true, - domain, 0, NULL, &sa_manager->bo); + r = amdgpu_bo_create(adev, size, align, true, domain, + 0, NULL, NULL, &sa_manager->bo); if (r) { dev_err(adev->dev, "(%d) failed to allocate bo for manager\n", r); return r; @@ -145,8 +145,13 @@ static uint32_t amdgpu_sa_get_ring_from_fence(struct fence *f) struct amd_sched_fence *s_fence; s_fence = to_amd_sched_fence(f); - if (s_fence) - return s_fence->scheduler->ring_id; + if (s_fence) { + struct amdgpu_ring *ring; + + ring = container_of(s_fence->sched, struct amdgpu_ring, sched); + return ring->idx; + } + a_fence = to_amdgpu_fence(f); if (a_fence) return a_fence->ring->idx; @@ -412,6 +417,26 @@ void amdgpu_sa_bo_free(struct amdgpu_device *adev, struct amdgpu_sa_bo **sa_bo, } #if defined(CONFIG_DEBUG_FS) + +static void amdgpu_sa_bo_dump_fence(struct fence *fence, struct seq_file *m) +{ + struct amdgpu_fence *a_fence = to_amdgpu_fence(fence); + struct amd_sched_fence *s_fence = to_amd_sched_fence(fence); + + if (a_fence) + seq_printf(m, " protected by 0x%016llx on ring %d", + a_fence->seq, a_fence->ring->idx); + + if (s_fence) { + struct amdgpu_ring *ring; + + + ring = container_of(s_fence->sched, struct amdgpu_ring, sched); + seq_printf(m, " protected by 0x%016x on ring %d", + s_fence->base.seqno, ring->idx); + } +} + void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager, struct seq_file *m) { @@ -428,18 +453,8 @@ void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager, } seq_printf(m, "[0x%010llx 0x%010llx] size %8lld", soffset, eoffset, eoffset - soffset); - if (i->fence) { - struct amdgpu_fence *a_fence = to_amdgpu_fence(i->fence); - struct amd_sched_fence *s_fence = to_amd_sched_fence(i->fence); - if (a_fence) - seq_printf(m, " protected by 0x%016llx on ring %d", - a_fence->seq, a_fence->ring->idx); - if (s_fence) - seq_printf(m, " protected by 0x%016x on ring %d", - s_fence->base.seqno, - s_fence->scheduler->ring_id); - - } + if (i->fence) + amdgpu_sa_bo_dump_fence(i->fence, m); seq_printf(m, "\n"); } spin_unlock(&sa_manager->wq.lock); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c index de98fbd2971e..2e946b2cad88 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c @@ -27,63 +27,48 @@ #include <drm/drmP.h> #include "amdgpu.h" -static struct fence *amdgpu_sched_dependency(struct amd_sched_job *job) +static struct fence *amdgpu_sched_dependency(struct amd_sched_job *sched_job) { - struct amdgpu_job *sched_job = (struct amdgpu_job *)job; - return amdgpu_sync_get_fence(&sched_job->ibs->sync); + struct amdgpu_job *job = to_amdgpu_job(sched_job); + return amdgpu_sync_get_fence(&job->ibs->sync); } -static struct fence *amdgpu_sched_run_job(struct amd_sched_job *job) +static struct fence *amdgpu_sched_run_job(struct amd_sched_job *sched_job) { - struct amdgpu_job *sched_job; - struct amdgpu_fence *fence; + struct amdgpu_fence *fence = NULL; + struct amdgpu_job *job; int r; - if (!job) { + if (!sched_job) { DRM_ERROR("job is null\n"); return NULL; } - sched_job = (struct amdgpu_job *)job; - mutex_lock(&sched_job->job_lock); - r = amdgpu_ib_schedule(sched_job->adev, - sched_job->num_ibs, - sched_job->ibs, - sched_job->base.owner); - if (r) + job = to_amdgpu_job(sched_job); + mutex_lock(&job->job_lock); + r = amdgpu_ib_schedule(job->adev, + job->num_ibs, + job->ibs, + job->base.owner); + if (r) { + DRM_ERROR("Error scheduling IBs (%d)\n", r); goto err; - fence = amdgpu_fence_ref(sched_job->ibs[sched_job->num_ibs - 1].fence); - - if (sched_job->free_job) - sched_job->free_job(sched_job); + } - mutex_unlock(&sched_job->job_lock); - return &fence->base; + fence = amdgpu_fence_ref(job->ibs[job->num_ibs - 1].fence); err: - DRM_ERROR("Run job error\n"); - mutex_unlock(&sched_job->job_lock); - job->sched->ops->process_job(job); - return NULL; -} + if (job->free_job) + job->free_job(job); -static void amdgpu_sched_process_job(struct amd_sched_job *job) -{ - struct amdgpu_job *sched_job; - - if (!job) { - DRM_ERROR("job is null\n"); - return; - } - sched_job = (struct amdgpu_job *)job; - /* after processing job, free memory */ - fence_put(&sched_job->base.s_fence->base); - kfree(sched_job); + mutex_unlock(&job->job_lock); + fence_put(&job->base.s_fence->base); + kfree(job); + return fence ? &fence->base : NULL; } struct amd_sched_backend_ops amdgpu_sched_ops = { .dependency = amdgpu_sched_dependency, .run_job = amdgpu_sched_run_job, - .process_job = amdgpu_sched_process_job }; int amdgpu_sched_ib_submit_kernel_helper(struct amdgpu_device *adev, @@ -100,7 +85,7 @@ int amdgpu_sched_ib_submit_kernel_helper(struct amdgpu_device *adev, kzalloc(sizeof(struct amdgpu_job), GFP_KERNEL); if (!job) return -ENOMEM; - job->base.sched = ring->scheduler; + job->base.sched = &ring->sched; job->base.s_entity = &adev->kernel_ctx.rings[ring->idx].entity; job->adev = adev; job->ibs = ibs; @@ -109,7 +94,7 @@ int amdgpu_sched_ib_submit_kernel_helper(struct amdgpu_device *adev, mutex_init(&job->job_lock); job->free_job = free_job; mutex_lock(&job->job_lock); - r = amd_sched_entity_push_job((struct amd_sched_job *)job); + r = amd_sched_entity_push_job(&job->base); if (r) { mutex_unlock(&job->job_lock); kfree(job); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c index 068aeaff7183..4921de15b451 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c @@ -65,8 +65,14 @@ static bool amdgpu_sync_same_dev(struct amdgpu_device *adev, struct fence *f) if (a_fence) return a_fence->ring->adev == adev; - if (s_fence) - return (struct amdgpu_device *)s_fence->scheduler->priv == adev; + + if (s_fence) { + struct amdgpu_ring *ring; + + ring = container_of(s_fence->sched, struct amdgpu_ring, sched); + return ring->adev == adev; + } + return false; } @@ -251,6 +257,20 @@ int amdgpu_sync_wait(struct amdgpu_sync *sync) fence_put(e->fence); kfree(e); } + + if (amdgpu_enable_semaphores) + return 0; + + for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { + struct amdgpu_fence *fence = sync->sync_to[i]; + if (!fence) + continue; + + r = fence_wait(&fence->base, false); + if (r) + return r; + } + return 0; } @@ -285,7 +305,8 @@ int amdgpu_sync_rings(struct amdgpu_sync *sync, return -EINVAL; } - if (amdgpu_enable_scheduler || (count >= AMDGPU_NUM_SYNCS)) { + if (amdgpu_enable_scheduler || !amdgpu_enable_semaphores || + (count >= AMDGPU_NUM_SYNCS)) { /* not enough room, wait manually */ r = fence_wait(&fence->base, false); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c index f80b1a43be8a..4865615e9c06 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c @@ -59,8 +59,9 @@ static void amdgpu_do_test_moves(struct amdgpu_device *adev) goto out_cleanup; } - r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, 0, - NULL, &vram_obj); + r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, + AMDGPU_GEM_DOMAIN_VRAM, 0, + NULL, NULL, &vram_obj); if (r) { DRM_ERROR("Failed to create VRAM object\n"); goto out_cleanup; @@ -80,7 +81,8 @@ static void amdgpu_do_test_moves(struct amdgpu_device *adev) struct fence *fence = NULL; r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, - AMDGPU_GEM_DOMAIN_GTT, 0, NULL, gtt_obj + i); + AMDGPU_GEM_DOMAIN_GTT, 0, NULL, + NULL, gtt_obj + i); if (r) { DRM_ERROR("Failed to create GTT object %d\n", i); goto out_lclean; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index b5abd5cde413..364cbe975332 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -861,7 +861,7 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) r = amdgpu_bo_create(adev, 256 * 1024, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, &adev->stollen_vga_memory); + NULL, NULL, &adev->stollen_vga_memory); if (r) { return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c index 482e66797ae6..5cc95f1a7dab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c @@ -247,7 +247,7 @@ int amdgpu_ucode_init_bo(struct amdgpu_device *adev) const struct common_firmware_header *header = NULL; err = amdgpu_bo_create(adev, adev->firmware.fw_size, PAGE_SIZE, true, - AMDGPU_GEM_DOMAIN_GTT, 0, NULL, bo); + AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, bo); if (err) { dev_err(adev->dev, "(%d) Firmware buffer allocate failed\n", err); err = -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 2cf6c6b06e3b..d0312364d950 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -156,7 +156,7 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev) r = amdgpu_bo_create(adev, bo_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, &adev->uvd.vcpu_bo); + NULL, NULL, &adev->uvd.vcpu_bo); if (r) { dev_err(adev->dev, "(%d) failed to allocate UVD bo\n", r); return r; @@ -543,46 +543,60 @@ static int amdgpu_uvd_cs_msg(struct amdgpu_uvd_cs_ctx *ctx, return -EINVAL; } - if (msg_type == 1) { + switch (msg_type) { + case 0: + /* it's a create msg, calc image size (width * height) */ + amdgpu_bo_kunmap(bo); + + /* try to alloc a new handle */ + for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { + if (atomic_read(&adev->uvd.handles[i]) == handle) { + DRM_ERROR("Handle 0x%x already in use!\n", handle); + return -EINVAL; + } + + if (!atomic_cmpxchg(&adev->uvd.handles[i], 0, handle)) { + adev->uvd.filp[i] = ctx->parser->filp; + return 0; + } + } + + DRM_ERROR("No more free UVD handles!\n"); + return -EINVAL; + + case 1: /* it's a decode msg, calc buffer sizes */ r = amdgpu_uvd_cs_msg_decode(msg, ctx->buf_sizes); amdgpu_bo_kunmap(bo); if (r) return r; - } else if (msg_type == 2) { + /* validate the handle */ + for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { + if (atomic_read(&adev->uvd.handles[i]) == handle) { + if (adev->uvd.filp[i] != ctx->parser->filp) { + DRM_ERROR("UVD handle collision detected!\n"); + return -EINVAL; + } + return 0; + } + } + + DRM_ERROR("Invalid UVD handle 0x%x!\n", handle); + return -ENOENT; + + case 2: /* it's a destroy msg, free the handle */ for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) atomic_cmpxchg(&adev->uvd.handles[i], handle, 0); amdgpu_bo_kunmap(bo); return 0; - } else { - /* it's a create msg */ - amdgpu_bo_kunmap(bo); - - if (msg_type != 0) { - DRM_ERROR("Illegal UVD message type (%d)!\n", msg_type); - return -EINVAL; - } - - /* it's a create msg, no special handling needed */ - } - - /* create or decode, validate the handle */ - for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { - if (atomic_read(&adev->uvd.handles[i]) == handle) - return 0; - } - /* handle not found try to alloc a new one */ - for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { - if (!atomic_cmpxchg(&adev->uvd.handles[i], 0, handle)) { - adev->uvd.filp[i] = ctx->parser->filp; - return 0; - } + default: + DRM_ERROR("Illegal UVD message type (%d)!\n", msg_type); + return -EINVAL; } - - DRM_ERROR("No more free UVD handles!\n"); + BUG(); return -EINVAL; } @@ -805,10 +819,10 @@ int amdgpu_uvd_ring_parse_cs(struct amdgpu_cs_parser *parser, uint32_t ib_idx) } static int amdgpu_uvd_free_job( - struct amdgpu_job *sched_job) + struct amdgpu_job *job) { - amdgpu_ib_free(sched_job->adev, sched_job->ibs); - kfree(sched_job->ibs); + amdgpu_ib_free(job->adev, job->ibs); + kfree(job->ibs); return 0; } @@ -905,7 +919,7 @@ int amdgpu_uvd_get_create_msg(struct amdgpu_ring *ring, uint32_t handle, r = amdgpu_bo_create(adev, 1024, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, &bo); + NULL, NULL, &bo); if (r) return r; @@ -954,7 +968,7 @@ int amdgpu_uvd_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle, r = amdgpu_bo_create(adev, 1024, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, &bo); + NULL, NULL, &bo); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c index 3cab96c42aa8..74f2038ac747 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c @@ -143,7 +143,7 @@ int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size) r = amdgpu_bo_create(adev, size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, &adev->vce.vcpu_bo); + NULL, NULL, &adev->vce.vcpu_bo); if (r) { dev_err(adev->dev, "(%d) failed to allocate VCE bo\n", r); return r; @@ -342,10 +342,10 @@ void amdgpu_vce_free_handles(struct amdgpu_device *adev, struct drm_file *filp) } static int amdgpu_vce_free_job( - struct amdgpu_job *sched_job) + struct amdgpu_job *job) { - amdgpu_ib_free(sched_job->adev, sched_job->ibs); - kfree(sched_job->ibs); + amdgpu_ib_free(job->adev, job->ibs); + kfree(job->ibs); return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index f68b7cdc370a..1e14531353e0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -316,12 +316,12 @@ static void amdgpu_vm_update_pages(struct amdgpu_device *adev, } } -int amdgpu_vm_free_job(struct amdgpu_job *sched_job) +int amdgpu_vm_free_job(struct amdgpu_job *job) { int i; - for (i = 0; i < sched_job->num_ibs; i++) - amdgpu_ib_free(sched_job->adev, &sched_job->ibs[i]); - kfree(sched_job->ibs); + for (i = 0; i < job->num_ibs; i++) + amdgpu_ib_free(job->adev, &job->ibs[i]); + kfree(job->ibs); return 0; } @@ -686,31 +686,6 @@ static int amdgpu_vm_update_ptes(struct amdgpu_device *adev, } /** - * amdgpu_vm_fence_pts - fence page tables after an update - * - * @vm: requested vm - * @start: start of GPU address range - * @end: end of GPU address range - * @fence: fence to use - * - * Fence the page tables in the range @start - @end (cayman+). - * - * Global and local mutex must be locked! - */ -static void amdgpu_vm_fence_pts(struct amdgpu_vm *vm, - uint64_t start, uint64_t end, - struct fence *fence) -{ - unsigned i; - - start >>= amdgpu_vm_block_size; - end >>= amdgpu_vm_block_size; - - for (i = start; i <= end; ++i) - amdgpu_bo_fence(vm->page_tables[i].bo, fence, true); -} - -/** * amdgpu_vm_bo_update_mapping - update a mapping in the vm page table * * @adev: amdgpu_device pointer @@ -813,8 +788,7 @@ static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev, if (r) goto error_free; - amdgpu_vm_fence_pts(vm, mapping->it.start, - mapping->it.last + 1, f); + amdgpu_bo_fence(vm->page_directory, f, true); if (fence) { fence_put(*fence); *fence = fence_get(f); @@ -855,7 +829,7 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev, int r; if (mem) { - addr = mem->start << PAGE_SHIFT; + addr = (u64)mem->start << PAGE_SHIFT; if (mem->mem_type != TTM_PL_TT) addr += adev->vm_manager.vram_base_offset; } else { @@ -1089,6 +1063,7 @@ int amdgpu_vm_bo_map(struct amdgpu_device *adev, /* walk over the address space and allocate the page tables */ for (pt_idx = saddr; pt_idx <= eaddr; ++pt_idx) { + struct reservation_object *resv = vm->page_directory->tbo.resv; struct amdgpu_bo *pt; if (vm->page_tables[pt_idx].bo) @@ -1097,11 +1072,13 @@ int amdgpu_vm_bo_map(struct amdgpu_device *adev, /* drop mutex to allocate and clear page table */ mutex_unlock(&vm->mutex); + ww_mutex_lock(&resv->lock, NULL); r = amdgpu_bo_create(adev, AMDGPU_VM_PTE_COUNT * 8, AMDGPU_GPU_PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_NO_CPU_ACCESS, - NULL, &pt); + NULL, resv, &pt); + ww_mutex_unlock(&resv->lock); if (r) goto error_free; @@ -1303,7 +1280,7 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm) r = amdgpu_bo_create(adev, pd_size, align, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_NO_CPU_ACCESS, - NULL, &vm->page_directory); + NULL, NULL, &vm->page_directory); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/cz_smc.c b/drivers/gpu/drm/amd/amdgpu/cz_smc.c index a72ffc7d6c26..e33180d3314a 100644 --- a/drivers/gpu/drm/amd/amdgpu/cz_smc.c +++ b/drivers/gpu/drm/amd/amdgpu/cz_smc.c @@ -814,7 +814,8 @@ int cz_smu_init(struct amdgpu_device *adev) * 3. map kernel virtual address */ ret = amdgpu_bo_create(adev, priv->toc_buffer.data_size, PAGE_SIZE, - true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, toc_buf); + true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, + toc_buf); if (ret) { dev_err(adev->dev, "(%d) SMC TOC buffer allocation failed\n", ret); @@ -822,7 +823,8 @@ int cz_smu_init(struct amdgpu_device *adev) } ret = amdgpu_bo_create(adev, priv->smu_buffer.data_size, PAGE_SIZE, - true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, smu_buf); + true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, + smu_buf); if (ret) { dev_err(adev->dev, "(%d) SMC Internal buffer allocation failed\n", ret); diff --git a/drivers/gpu/drm/amd/amdgpu/fiji_smc.c b/drivers/gpu/drm/amd/amdgpu/fiji_smc.c index 322edea65857..bda1249eb871 100644 --- a/drivers/gpu/drm/amd/amdgpu/fiji_smc.c +++ b/drivers/gpu/drm/amd/amdgpu/fiji_smc.c @@ -764,7 +764,7 @@ int fiji_smu_init(struct amdgpu_device *adev) ret = amdgpu_bo_create(adev, image_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, toc_buf); + NULL, NULL, toc_buf); if (ret) { DRM_ERROR("Failed to allocate memory for TOC buffer\n"); return -ENOMEM; @@ -774,7 +774,7 @@ int fiji_smu_init(struct amdgpu_device *adev) ret = amdgpu_bo_create(adev, smu_internal_buffer_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, smu_buf); + NULL, NULL, smu_buf); if (ret) { DRM_ERROR("Failed to allocate memory for SMU internal buffer\n"); return -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 4bd1e5cf65ca..e992bf2ff66c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -3206,7 +3206,7 @@ static int gfx_v7_0_mec_init(struct amdgpu_device *adev) r = amdgpu_bo_create(adev, adev->gfx.mec.num_mec *adev->gfx.mec.num_pipe * MEC_HPD_SIZE * 2, PAGE_SIZE, true, - AMDGPU_GEM_DOMAIN_GTT, 0, NULL, + AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, &adev->gfx.mec.hpd_eop_obj); if (r) { dev_warn(adev->dev, "(%d) create HDP EOP bo failed\n", r); @@ -3373,7 +3373,7 @@ static int gfx_v7_0_cp_compute_resume(struct amdgpu_device *adev) r = amdgpu_bo_create(adev, sizeof(struct bonaire_mqd), PAGE_SIZE, true, - AMDGPU_GEM_DOMAIN_GTT, 0, NULL, + AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, &ring->mqd_obj); if (r) { dev_warn(adev->dev, "(%d) create MQD bo failed\n", r); @@ -3610,41 +3610,6 @@ static int gfx_v7_0_cp_resume(struct amdgpu_device *adev) return 0; } -static void gfx_v7_0_ce_sync_me(struct amdgpu_ring *ring) -{ - struct amdgpu_device *adev = ring->adev; - u64 gpu_addr = adev->wb.gpu_addr + adev->gfx.ce_sync_offs * 4; - - /* instruct DE to set a magic number */ - amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); - amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | - WRITE_DATA_DST_SEL(5))); - amdgpu_ring_write(ring, gpu_addr & 0xfffffffc); - amdgpu_ring_write(ring, upper_32_bits(gpu_addr) & 0xffffffff); - amdgpu_ring_write(ring, 1); - - /* let CE wait till condition satisfied */ - amdgpu_ring_write(ring, PACKET3(PACKET3_WAIT_REG_MEM, 5)); - amdgpu_ring_write(ring, (WAIT_REG_MEM_OPERATION(0) | /* wait */ - WAIT_REG_MEM_MEM_SPACE(1) | /* memory */ - WAIT_REG_MEM_FUNCTION(3) | /* == */ - WAIT_REG_MEM_ENGINE(2))); /* ce */ - amdgpu_ring_write(ring, gpu_addr & 0xfffffffc); - amdgpu_ring_write(ring, upper_32_bits(gpu_addr) & 0xffffffff); - amdgpu_ring_write(ring, 1); - amdgpu_ring_write(ring, 0xffffffff); - amdgpu_ring_write(ring, 4); /* poll interval */ - - /* instruct CE to reset wb of ce_sync to zero */ - amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); - amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(2) | - WRITE_DATA_DST_SEL(5) | - WR_CONFIRM)); - amdgpu_ring_write(ring, gpu_addr & 0xfffffffc); - amdgpu_ring_write(ring, upper_32_bits(gpu_addr) & 0xffffffff); - amdgpu_ring_write(ring, 0); -} - /* * vm * VMID 0 is the physical GPU addresses as used by the kernel. @@ -3663,6 +3628,13 @@ static void gfx_v7_0_ring_emit_vm_flush(struct amdgpu_ring *ring, unsigned vm_id, uint64_t pd_addr) { int usepfp = (ring->type == AMDGPU_RING_TYPE_GFX); + if (usepfp) { + /* synce CE with ME to prevent CE fetch CEIB before context switch done */ + amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); + amdgpu_ring_write(ring, 0); + amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); + amdgpu_ring_write(ring, 0); + } amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(usepfp) | @@ -3703,7 +3675,10 @@ static void gfx_v7_0_ring_emit_vm_flush(struct amdgpu_ring *ring, amdgpu_ring_write(ring, 0x0); /* synce CE with ME to prevent CE fetch CEIB before context switch done */ - gfx_v7_0_ce_sync_me(ring); + amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); + amdgpu_ring_write(ring, 0); + amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); + amdgpu_ring_write(ring, 0); } } @@ -3788,7 +3763,8 @@ static int gfx_v7_0_rlc_init(struct amdgpu_device *adev) r = amdgpu_bo_create(adev, dws * 4, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, &adev->gfx.rlc.save_restore_obj); + NULL, NULL, + &adev->gfx.rlc.save_restore_obj); if (r) { dev_warn(adev->dev, "(%d) create RLC sr bo failed\n", r); return r; @@ -3831,7 +3807,8 @@ static int gfx_v7_0_rlc_init(struct amdgpu_device *adev) r = amdgpu_bo_create(adev, dws * 4, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, &adev->gfx.rlc.clear_state_obj); + NULL, NULL, + &adev->gfx.rlc.clear_state_obj); if (r) { dev_warn(adev->dev, "(%d) create RLC c bo failed\n", r); gfx_v7_0_rlc_fini(adev); @@ -3870,7 +3847,8 @@ static int gfx_v7_0_rlc_init(struct amdgpu_device *adev) r = amdgpu_bo_create(adev, adev->gfx.rlc.cp_table_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, &adev->gfx.rlc.cp_table_obj); + NULL, NULL, + &adev->gfx.rlc.cp_table_obj); if (r) { dev_warn(adev->dev, "(%d) create RLC cp table bo failed\n", r); gfx_v7_0_rlc_fini(adev); @@ -4802,12 +4780,6 @@ static int gfx_v7_0_sw_init(void *handle) return r; } - r = amdgpu_wb_get(adev, &adev->gfx.ce_sync_offs); - if (r) { - DRM_ERROR("(%d) gfx.ce_sync_offs wb alloc failed\n", r); - return r; - } - for (i = 0; i < adev->gfx.num_gfx_rings; i++) { ring = &adev->gfx.gfx_ring[i]; ring->ring_obj = NULL; @@ -4851,21 +4823,21 @@ static int gfx_v7_0_sw_init(void *handle) r = amdgpu_bo_create(adev, adev->gds.mem.gfx_partition_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_GDS, 0, - NULL, &adev->gds.gds_gfx_bo); + NULL, NULL, &adev->gds.gds_gfx_bo); if (r) return r; r = amdgpu_bo_create(adev, adev->gds.gws.gfx_partition_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_GWS, 0, - NULL, &adev->gds.gws_gfx_bo); + NULL, NULL, &adev->gds.gws_gfx_bo); if (r) return r; r = amdgpu_bo_create(adev, adev->gds.oa.gfx_partition_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_OA, 0, - NULL, &adev->gds.oa_gfx_bo); + NULL, NULL, &adev->gds.oa_gfx_bo); if (r) return r; @@ -4886,8 +4858,6 @@ static int gfx_v7_0_sw_fini(void *handle) for (i = 0; i < adev->gfx.num_compute_rings; i++) amdgpu_ring_fini(&adev->gfx.compute_ring[i]); - amdgpu_wb_free(adev, adev->gfx.ce_sync_offs); - gfx_v7_0_cp_compute_fini(adev); gfx_v7_0_rlc_fini(adev); gfx_v7_0_mec_fini(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 53f07439a512..cb4f68f53f24 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -868,7 +868,7 @@ static int gfx_v8_0_mec_init(struct amdgpu_device *adev) r = amdgpu_bo_create(adev, adev->gfx.mec.num_mec *adev->gfx.mec.num_pipe * MEC_HPD_SIZE * 2, PAGE_SIZE, true, - AMDGPU_GEM_DOMAIN_GTT, 0, NULL, + AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, &adev->gfx.mec.hpd_eop_obj); if (r) { dev_warn(adev->dev, "(%d) create HDP EOP bo failed\n", r); @@ -940,12 +940,6 @@ static int gfx_v8_0_sw_init(void *handle) return r; } - r = amdgpu_wb_get(adev, &adev->gfx.ce_sync_offs); - if (r) { - DRM_ERROR("(%d) gfx.ce_sync_offs wb alloc failed\n", r); - return r; - } - /* set up the gfx ring */ for (i = 0; i < adev->gfx.num_gfx_rings; i++) { ring = &adev->gfx.gfx_ring[i]; @@ -995,21 +989,21 @@ static int gfx_v8_0_sw_init(void *handle) /* reserve GDS, GWS and OA resource for gfx */ r = amdgpu_bo_create(adev, adev->gds.mem.gfx_partition_size, PAGE_SIZE, true, - AMDGPU_GEM_DOMAIN_GDS, 0, + AMDGPU_GEM_DOMAIN_GDS, 0, NULL, NULL, &adev->gds.gds_gfx_bo); if (r) return r; r = amdgpu_bo_create(adev, adev->gds.gws.gfx_partition_size, PAGE_SIZE, true, - AMDGPU_GEM_DOMAIN_GWS, 0, + AMDGPU_GEM_DOMAIN_GWS, 0, NULL, NULL, &adev->gds.gws_gfx_bo); if (r) return r; r = amdgpu_bo_create(adev, adev->gds.oa.gfx_partition_size, PAGE_SIZE, true, - AMDGPU_GEM_DOMAIN_OA, 0, + AMDGPU_GEM_DOMAIN_OA, 0, NULL, NULL, &adev->gds.oa_gfx_bo); if (r) return r; @@ -1033,8 +1027,6 @@ static int gfx_v8_0_sw_fini(void *handle) for (i = 0; i < adev->gfx.num_compute_rings; i++) amdgpu_ring_fini(&adev->gfx.compute_ring[i]); - amdgpu_wb_free(adev, adev->gfx.ce_sync_offs); - gfx_v8_0_mec_fini(adev); return 0; @@ -3106,7 +3098,7 @@ static int gfx_v8_0_cp_compute_resume(struct amdgpu_device *adev) sizeof(struct vi_mqd), PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, - &ring->mqd_obj); + NULL, &ring->mqd_obj); if (r) { dev_warn(adev->dev, "(%d) create MQD bo failed\n", r); return r; @@ -3965,6 +3957,7 @@ static void gfx_v8_0_ring_emit_fence_gfx(struct amdgpu_ring *ring, u64 addr, DATA_SEL(write64bit ? 2 : 1) | INT_SEL(int_sel ? 2 : 0)); amdgpu_ring_write(ring, lower_32_bits(seq)); amdgpu_ring_write(ring, upper_32_bits(seq)); + } /** @@ -4005,49 +3998,34 @@ static bool gfx_v8_0_ring_emit_semaphore(struct amdgpu_ring *ring, return true; } -static void gfx_v8_0_ce_sync_me(struct amdgpu_ring *ring) +static void gfx_v8_0_ring_emit_vm_flush(struct amdgpu_ring *ring, + unsigned vm_id, uint64_t pd_addr) { - struct amdgpu_device *adev = ring->adev; - u64 gpu_addr = adev->wb.gpu_addr + adev->gfx.ce_sync_offs * 4; - - /* instruct DE to set a magic number */ - amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); - amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | - WRITE_DATA_DST_SEL(5))); - amdgpu_ring_write(ring, gpu_addr & 0xfffffffc); - amdgpu_ring_write(ring, upper_32_bits(gpu_addr) & 0xffffffff); - amdgpu_ring_write(ring, 1); + int usepfp = (ring->type == AMDGPU_RING_TYPE_GFX); + uint32_t seq = ring->fence_drv.sync_seq[ring->idx]; + uint64_t addr = ring->fence_drv.gpu_addr; - /* let CE wait till condition satisfied */ amdgpu_ring_write(ring, PACKET3(PACKET3_WAIT_REG_MEM, 5)); - amdgpu_ring_write(ring, (WAIT_REG_MEM_OPERATION(0) | /* wait */ - WAIT_REG_MEM_MEM_SPACE(1) | /* memory */ - WAIT_REG_MEM_FUNCTION(3) | /* == */ - WAIT_REG_MEM_ENGINE(2))); /* ce */ - amdgpu_ring_write(ring, gpu_addr & 0xfffffffc); - amdgpu_ring_write(ring, upper_32_bits(gpu_addr) & 0xffffffff); - amdgpu_ring_write(ring, 1); + amdgpu_ring_write(ring, (WAIT_REG_MEM_MEM_SPACE(1) | /* memory */ + WAIT_REG_MEM_FUNCTION(3))); /* equal */ + amdgpu_ring_write(ring, addr & 0xfffffffc); + amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff); + amdgpu_ring_write(ring, seq); amdgpu_ring_write(ring, 0xffffffff); amdgpu_ring_write(ring, 4); /* poll interval */ - /* instruct CE to reset wb of ce_sync to zero */ - amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); - amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(2) | - WRITE_DATA_DST_SEL(5) | - WR_CONFIRM)); - amdgpu_ring_write(ring, gpu_addr & 0xfffffffc); - amdgpu_ring_write(ring, upper_32_bits(gpu_addr) & 0xffffffff); - amdgpu_ring_write(ring, 0); -} - -static void gfx_v8_0_ring_emit_vm_flush(struct amdgpu_ring *ring, - unsigned vm_id, uint64_t pd_addr) -{ - int usepfp = (ring->type == AMDGPU_RING_TYPE_GFX); + if (usepfp) { + /* synce CE with ME to prevent CE fetch CEIB before context switch done */ + amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); + amdgpu_ring_write(ring, 0); + amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); + amdgpu_ring_write(ring, 0); + } amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(usepfp) | - WRITE_DATA_DST_SEL(0))); + WRITE_DATA_DST_SEL(0)) | + WR_CONFIRM); if (vm_id < 8) { amdgpu_ring_write(ring, (mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR + vm_id)); @@ -4083,9 +4061,10 @@ static void gfx_v8_0_ring_emit_vm_flush(struct amdgpu_ring *ring, /* sync PFP to ME, otherwise we might get invalid PFP reads */ amdgpu_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0)); amdgpu_ring_write(ring, 0x0); - - /* synce CE with ME to prevent CE fetch CEIB before context switch done */ - gfx_v8_0_ce_sync_me(ring); + amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); + amdgpu_ring_write(ring, 0); + amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); + amdgpu_ring_write(ring, 0); } } diff --git a/drivers/gpu/drm/amd/amdgpu/iceland_smc.c b/drivers/gpu/drm/amd/amdgpu/iceland_smc.c index c900aa942ade..966d4b2ed9da 100644 --- a/drivers/gpu/drm/amd/amdgpu/iceland_smc.c +++ b/drivers/gpu/drm/amd/amdgpu/iceland_smc.c @@ -625,7 +625,7 @@ int iceland_smu_init(struct amdgpu_device *adev) ret = amdgpu_bo_create(adev, image_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, toc_buf); + NULL, NULL, toc_buf); if (ret) { DRM_ERROR("Failed to allocate memory for TOC buffer\n"); return -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdgpu/tonga_smc.c b/drivers/gpu/drm/amd/amdgpu/tonga_smc.c index 1f5ac941a610..5421309c1862 100644 --- a/drivers/gpu/drm/amd/amdgpu/tonga_smc.c +++ b/drivers/gpu/drm/amd/amdgpu/tonga_smc.c @@ -763,7 +763,7 @@ int tonga_smu_init(struct amdgpu_device *adev) ret = amdgpu_bo_create(adev, image_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, toc_buf); + NULL, NULL, toc_buf); if (ret) { DRM_ERROR("Failed to allocate memory for TOC buffer\n"); return -ENOMEM; @@ -773,7 +773,7 @@ int tonga_smu_init(struct amdgpu_device *adev) ret = amdgpu_bo_create(adev, smu_internal_buffer_size, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - NULL, smu_buf); + NULL, NULL, smu_buf); if (ret) { DRM_ERROR("Failed to allocate memory for SMU internal buffer\n"); return -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c index 5fac5da694f0..ed50dd725788 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c @@ -224,11 +224,11 @@ static int uvd_v4_2_suspend(void *handle) int r; struct amdgpu_device *adev = (struct amdgpu_device *)handle; - r = uvd_v4_2_hw_fini(adev); + r = amdgpu_uvd_suspend(adev); if (r) return r; - r = amdgpu_uvd_suspend(adev); + r = uvd_v4_2_hw_fini(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c index 2d5c59c318af..9ad8b9906c0b 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c @@ -220,11 +220,11 @@ static int uvd_v5_0_suspend(void *handle) int r; struct amdgpu_device *adev = (struct amdgpu_device *)handle; - r = uvd_v5_0_hw_fini(adev); + r = amdgpu_uvd_suspend(adev); if (r) return r; - r = amdgpu_uvd_suspend(adev); + r = uvd_v5_0_hw_fini(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c index d9f553fce531..7e9934fa4193 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c @@ -214,14 +214,16 @@ static int uvd_v6_0_suspend(void *handle) int r; struct amdgpu_device *adev = (struct amdgpu_device *)handle; + /* Skip this for APU for now */ + if (!(adev->flags & AMD_IS_APU)) { + r = amdgpu_uvd_suspend(adev); + if (r) + return r; + } r = uvd_v6_0_hw_fini(adev); if (r) return r; - r = amdgpu_uvd_suspend(adev); - if (r) - return r; - return r; } @@ -230,10 +232,12 @@ static int uvd_v6_0_resume(void *handle) int r; struct amdgpu_device *adev = (struct amdgpu_device *)handle; - r = amdgpu_uvd_resume(adev); - if (r) - return r; - + /* Skip this for APU for now */ + if (!(adev->flags & AMD_IS_APU)) { + r = amdgpu_uvd_resume(adev); + if (r) + return r; + } r = uvd_v6_0_hw_init(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c index 552d9e75ad1b..b55ceb14fdcd 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.c +++ b/drivers/gpu/drm/amd/amdgpu/vi.c @@ -1400,7 +1400,8 @@ static int vi_common_early_init(void *handle) case CHIP_CARRIZO: adev->has_uvd = true; adev->cg_flags = 0; - adev->pg_flags = AMDGPU_PG_SUPPORT_UVD | AMDGPU_PG_SUPPORT_VCE; + /* Disable UVD pg */ + adev->pg_flags = /* AMDGPU_PG_SUPPORT_UVD | */AMDGPU_PG_SUPPORT_VCE; adev->external_rev_id = adev->rev_id + 0x1; if (amdgpu_smc_load_fw && smc_enabled) adev->firmware.smu_load = true; diff --git a/drivers/gpu/drm/amd/scheduler/gpu_sched_trace.h b/drivers/gpu/drm/amd/scheduler/gpu_sched_trace.h new file mode 100644 index 000000000000..144f50acc971 --- /dev/null +++ b/drivers/gpu/drm/amd/scheduler/gpu_sched_trace.h @@ -0,0 +1,41 @@ +#if !defined(_GPU_SCHED_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _GPU_SCHED_TRACE_H_ + +#include <linux/stringify.h> +#include <linux/types.h> +#include <linux/tracepoint.h> + +#include <drm/drmP.h> + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM gpu_sched +#define TRACE_INCLUDE_FILE gpu_sched_trace + +TRACE_EVENT(amd_sched_job, + TP_PROTO(struct amd_sched_job *sched_job), + TP_ARGS(sched_job), + TP_STRUCT__entry( + __field(struct amd_sched_entity *, entity) + __field(const char *, name) + __field(u32, job_count) + __field(int, hw_job_count) + ), + + TP_fast_assign( + __entry->entity = sched_job->s_entity; + __entry->name = sched_job->sched->name; + __entry->job_count = kfifo_len( + &sched_job->s_entity->job_queue) / sizeof(sched_job); + __entry->hw_job_count = atomic_read( + &sched_job->sched->hw_rq_count); + ), + TP_printk("entity=%p, ring=%s, job count:%u, hw job count:%d", + __entry->entity, __entry->name, __entry->job_count, + __entry->hw_job_count) +); +#endif + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#include <trace/define_trace.h> diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c index 9259f1b6664c..3697eeeecf82 100644 --- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c +++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c @@ -27,6 +27,9 @@ #include <drm/drmP.h> #include "gpu_scheduler.h" +#define CREATE_TRACE_POINTS +#include "gpu_sched_trace.h" + static struct amd_sched_job * amd_sched_entity_pop_job(struct amd_sched_entity *entity); static void amd_sched_wakeup(struct amd_gpu_scheduler *sched); @@ -65,29 +68,29 @@ static struct amd_sched_job * amd_sched_rq_select_job(struct amd_sched_rq *rq) { struct amd_sched_entity *entity; - struct amd_sched_job *job; + struct amd_sched_job *sched_job; spin_lock(&rq->lock); entity = rq->current_entity; if (entity) { list_for_each_entry_continue(entity, &rq->entities, list) { - job = amd_sched_entity_pop_job(entity); - if (job) { + sched_job = amd_sched_entity_pop_job(entity); + if (sched_job) { rq->current_entity = entity; spin_unlock(&rq->lock); - return job; + return sched_job; } } } list_for_each_entry(entity, &rq->entities, list) { - job = amd_sched_entity_pop_job(entity); - if (job) { + sched_job = amd_sched_entity_pop_job(entity); + if (sched_job) { rq->current_entity = entity; spin_unlock(&rq->lock); - return job; + return sched_job; } if (entity == rq->current_entity) @@ -115,23 +118,27 @@ int amd_sched_entity_init(struct amd_gpu_scheduler *sched, struct amd_sched_rq *rq, uint32_t jobs) { + int r; + if (!(sched && entity && rq)) return -EINVAL; memset(entity, 0, sizeof(struct amd_sched_entity)); - entity->belongto_rq = rq; - entity->scheduler = sched; - entity->fence_context = fence_context_alloc(1); - if(kfifo_alloc(&entity->job_queue, - jobs * sizeof(void *), - GFP_KERNEL)) - return -EINVAL; + INIT_LIST_HEAD(&entity->list); + entity->rq = rq; + entity->sched = sched; spin_lock_init(&entity->queue_lock); + r = kfifo_alloc(&entity->job_queue, jobs * sizeof(void *), GFP_KERNEL); + if (r) + return r; + atomic_set(&entity->fence_seq, 0); + entity->fence_context = fence_context_alloc(1); /* Add the entity to the run queue */ amd_sched_rq_add_entity(rq, entity); + return 0; } @@ -146,8 +153,8 @@ int amd_sched_entity_init(struct amd_gpu_scheduler *sched, static bool amd_sched_entity_is_initialized(struct amd_gpu_scheduler *sched, struct amd_sched_entity *entity) { - return entity->scheduler == sched && - entity->belongto_rq != NULL; + return entity->sched == sched && + entity->rq != NULL; } /** @@ -177,7 +184,7 @@ static bool amd_sched_entity_is_idle(struct amd_sched_entity *entity) void amd_sched_entity_fini(struct amd_gpu_scheduler *sched, struct amd_sched_entity *entity) { - struct amd_sched_rq *rq = entity->belongto_rq; + struct amd_sched_rq *rq = entity->rq; if (!amd_sched_entity_is_initialized(sched, entity)) return; @@ -198,22 +205,22 @@ static void amd_sched_entity_wakeup(struct fence *f, struct fence_cb *cb) container_of(cb, struct amd_sched_entity, cb); entity->dependency = NULL; fence_put(f); - amd_sched_wakeup(entity->scheduler); + amd_sched_wakeup(entity->sched); } static struct amd_sched_job * amd_sched_entity_pop_job(struct amd_sched_entity *entity) { - struct amd_gpu_scheduler *sched = entity->scheduler; - struct amd_sched_job *job; + struct amd_gpu_scheduler *sched = entity->sched; + struct amd_sched_job *sched_job; if (ACCESS_ONCE(entity->dependency)) return NULL; - if (!kfifo_out_peek(&entity->job_queue, &job, sizeof(job))) + if (!kfifo_out_peek(&entity->job_queue, &sched_job, sizeof(sched_job))) return NULL; - while ((entity->dependency = sched->ops->dependency(job))) { + while ((entity->dependency = sched->ops->dependency(sched_job))) { if (fence_add_callback(entity->dependency, &entity->cb, amd_sched_entity_wakeup)) @@ -222,32 +229,33 @@ amd_sched_entity_pop_job(struct amd_sched_entity *entity) return NULL; } - return job; + return sched_job; } /** * Helper to submit a job to the job queue * - * @job The pointer to job required to submit + * @sched_job The pointer to job required to submit * * Returns true if we could submit the job. */ -static bool amd_sched_entity_in(struct amd_sched_job *job) +static bool amd_sched_entity_in(struct amd_sched_job *sched_job) { - struct amd_sched_entity *entity = job->s_entity; + struct amd_sched_entity *entity = sched_job->s_entity; bool added, first = false; spin_lock(&entity->queue_lock); - added = kfifo_in(&entity->job_queue, &job, sizeof(job)) == sizeof(job); + added = kfifo_in(&entity->job_queue, &sched_job, + sizeof(sched_job)) == sizeof(sched_job); - if (added && kfifo_len(&entity->job_queue) == sizeof(job)) + if (added && kfifo_len(&entity->job_queue) == sizeof(sched_job)) first = true; spin_unlock(&entity->queue_lock); /* first job wakes up scheduler */ if (first) - amd_sched_wakeup(job->sched); + amd_sched_wakeup(sched_job->sched); return added; } @@ -255,7 +263,7 @@ static bool amd_sched_entity_in(struct amd_sched_job *job) /** * Submit a job to the job queue * - * @job The pointer to job required to submit + * @sched_job The pointer to job required to submit * * Returns 0 for success, negative error code otherwise. */ @@ -271,9 +279,9 @@ int amd_sched_entity_push_job(struct amd_sched_job *sched_job) fence_get(&fence->base); sched_job->s_fence = fence; - wait_event(entity->scheduler->job_scheduled, + wait_event(entity->sched->job_scheduled, amd_sched_entity_in(sched_job)); - + trace_amd_sched_job(sched_job); return 0; } @@ -301,30 +309,28 @@ static void amd_sched_wakeup(struct amd_gpu_scheduler *sched) static struct amd_sched_job * amd_sched_select_job(struct amd_gpu_scheduler *sched) { - struct amd_sched_job *job; + struct amd_sched_job *sched_job; if (!amd_sched_ready(sched)) return NULL; /* Kernel run queue has higher priority than normal run queue*/ - job = amd_sched_rq_select_job(&sched->kernel_rq); - if (job == NULL) - job = amd_sched_rq_select_job(&sched->sched_rq); + sched_job = amd_sched_rq_select_job(&sched->kernel_rq); + if (sched_job == NULL) + sched_job = amd_sched_rq_select_job(&sched->sched_rq); - return job; + return sched_job; } static void amd_sched_process_job(struct fence *f, struct fence_cb *cb) { - struct amd_sched_job *sched_job = - container_of(cb, struct amd_sched_job, cb); - struct amd_gpu_scheduler *sched; + struct amd_sched_fence *s_fence = + container_of(cb, struct amd_sched_fence, cb); + struct amd_gpu_scheduler *sched = s_fence->sched; - sched = sched_job->sched; - amd_sched_fence_signal(sched_job->s_fence); atomic_dec(&sched->hw_rq_count); - fence_put(&sched_job->s_fence->base); - sched->ops->process_job(sched_job); + amd_sched_fence_signal(s_fence); + fence_put(&s_fence->base); wake_up_interruptible(&sched->wake_up_worker); } @@ -338,87 +344,82 @@ static int amd_sched_main(void *param) while (!kthread_should_stop()) { struct amd_sched_entity *entity; - struct amd_sched_job *job; + struct amd_sched_fence *s_fence; + struct amd_sched_job *sched_job; struct fence *fence; wait_event_interruptible(sched->wake_up_worker, kthread_should_stop() || - (job = amd_sched_select_job(sched))); + (sched_job = amd_sched_select_job(sched))); - if (!job) + if (!sched_job) continue; - entity = job->s_entity; + entity = sched_job->s_entity; + s_fence = sched_job->s_fence; atomic_inc(&sched->hw_rq_count); - fence = sched->ops->run_job(job); + fence = sched->ops->run_job(sched_job); if (fence) { - r = fence_add_callback(fence, &job->cb, + r = fence_add_callback(fence, &s_fence->cb, amd_sched_process_job); if (r == -ENOENT) - amd_sched_process_job(fence, &job->cb); + amd_sched_process_job(fence, &s_fence->cb); else if (r) DRM_ERROR("fence add callback failed (%d)\n", r); fence_put(fence); + } else { + DRM_ERROR("Failed to run job!\n"); + amd_sched_process_job(NULL, &s_fence->cb); } - count = kfifo_out(&entity->job_queue, &job, sizeof(job)); - WARN_ON(count != sizeof(job)); + count = kfifo_out(&entity->job_queue, &sched_job, + sizeof(sched_job)); + WARN_ON(count != sizeof(sched_job)); wake_up(&sched->job_scheduled); } return 0; } /** - * Create a gpu scheduler + * Init a gpu scheduler instance * + * @sched The pointer to the scheduler * @ops The backend operations for this scheduler. - * @ring The the ring id for the scheduler. * @hw_submissions Number of hw submissions to do. + * @name Name used for debugging * - * Return the pointer to scheduler for success, otherwise return NULL + * Return 0 on success, otherwise error code. */ -struct amd_gpu_scheduler *amd_sched_create(struct amd_sched_backend_ops *ops, - unsigned ring, unsigned hw_submission, - void *priv) +int amd_sched_init(struct amd_gpu_scheduler *sched, + struct amd_sched_backend_ops *ops, + unsigned hw_submission, const char *name) { - struct amd_gpu_scheduler *sched; - - sched = kzalloc(sizeof(struct amd_gpu_scheduler), GFP_KERNEL); - if (!sched) - return NULL; - sched->ops = ops; - sched->ring_id = ring; sched->hw_submission_limit = hw_submission; - sched->priv = priv; - snprintf(sched->name, sizeof(sched->name), "amdgpu[%d]", ring); + sched->name = name; amd_sched_rq_init(&sched->sched_rq); amd_sched_rq_init(&sched->kernel_rq); init_waitqueue_head(&sched->wake_up_worker); init_waitqueue_head(&sched->job_scheduled); atomic_set(&sched->hw_rq_count, 0); + /* Each scheduler will run on a seperate kernel thread */ sched->thread = kthread_run(amd_sched_main, sched, sched->name); if (IS_ERR(sched->thread)) { - DRM_ERROR("Failed to create scheduler for id %d.\n", ring); - kfree(sched); - return NULL; + DRM_ERROR("Failed to create scheduler for %s.\n", name); + return PTR_ERR(sched->thread); } - return sched; + return 0; } /** * Destroy a gpu scheduler * * @sched The pointer to the scheduler - * - * return 0 if succeed. -1 if failed. */ -int amd_sched_destroy(struct amd_gpu_scheduler *sched) +void amd_sched_fini(struct amd_gpu_scheduler *sched) { kthread_stop(sched->thread); - kfree(sched); - return 0; } diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h index 2af0e4d4d817..80b64dc22214 100644 --- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h +++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h @@ -38,13 +38,15 @@ struct amd_sched_rq; */ struct amd_sched_entity { struct list_head list; - struct amd_sched_rq *belongto_rq; - atomic_t fence_seq; - /* the job_queue maintains the jobs submitted by clients */ - struct kfifo job_queue; + struct amd_sched_rq *rq; + struct amd_gpu_scheduler *sched; + spinlock_t queue_lock; - struct amd_gpu_scheduler *scheduler; + struct kfifo job_queue; + + atomic_t fence_seq; uint64_t fence_context; + struct fence *dependency; struct fence_cb cb; }; @@ -62,13 +64,13 @@ struct amd_sched_rq { struct amd_sched_fence { struct fence base; - struct amd_gpu_scheduler *scheduler; + struct fence_cb cb; + struct amd_gpu_scheduler *sched; spinlock_t lock; void *owner; }; struct amd_sched_job { - struct fence_cb cb; struct amd_gpu_scheduler *sched; struct amd_sched_entity *s_entity; struct amd_sched_fence *s_fence; @@ -91,32 +93,29 @@ static inline struct amd_sched_fence *to_amd_sched_fence(struct fence *f) * these functions should be implemented in driver side */ struct amd_sched_backend_ops { - struct fence *(*dependency)(struct amd_sched_job *job); - struct fence *(*run_job)(struct amd_sched_job *job); - void (*process_job)(struct amd_sched_job *job); + struct fence *(*dependency)(struct amd_sched_job *sched_job); + struct fence *(*run_job)(struct amd_sched_job *sched_job); }; /** * One scheduler is implemented for each hardware ring */ struct amd_gpu_scheduler { - struct task_struct *thread; + struct amd_sched_backend_ops *ops; + uint32_t hw_submission_limit; + const char *name; struct amd_sched_rq sched_rq; struct amd_sched_rq kernel_rq; - atomic_t hw_rq_count; - struct amd_sched_backend_ops *ops; - uint32_t ring_id; wait_queue_head_t wake_up_worker; wait_queue_head_t job_scheduled; - uint32_t hw_submission_limit; - char name[20]; - void *priv; + atomic_t hw_rq_count; + struct task_struct *thread; }; -struct amd_gpu_scheduler * -amd_sched_create(struct amd_sched_backend_ops *ops, - uint32_t ring, uint32_t hw_submission, void *priv); -int amd_sched_destroy(struct amd_gpu_scheduler *sched); +int amd_sched_init(struct amd_gpu_scheduler *sched, + struct amd_sched_backend_ops *ops, + uint32_t hw_submission, const char *name); +void amd_sched_fini(struct amd_gpu_scheduler *sched); int amd_sched_entity_init(struct amd_gpu_scheduler *sched, struct amd_sched_entity *entity, diff --git a/drivers/gpu/drm/amd/scheduler/sched_fence.c b/drivers/gpu/drm/amd/scheduler/sched_fence.c index e62c37920e11..d802638094f4 100644 --- a/drivers/gpu/drm/amd/scheduler/sched_fence.c +++ b/drivers/gpu/drm/amd/scheduler/sched_fence.c @@ -36,7 +36,7 @@ struct amd_sched_fence *amd_sched_fence_create(struct amd_sched_entity *s_entity if (fence == NULL) return NULL; fence->owner = owner; - fence->scheduler = s_entity->scheduler; + fence->sched = s_entity->sched; spin_lock_init(&fence->lock); seq = atomic_inc_return(&s_entity->fence_seq); @@ -63,7 +63,7 @@ static const char *amd_sched_fence_get_driver_name(struct fence *fence) static const char *amd_sched_fence_get_timeline_name(struct fence *f) { struct amd_sched_fence *fence = to_amd_sched_fence(f); - return (const char *)fence->scheduler->name; + return (const char *)fence->sched->name; } static bool amd_sched_fence_enable_signaling(struct fence *f) diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 9a860ca1e9d7..d93e7378c077 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -520,7 +520,8 @@ EXPORT_SYMBOL(drm_ioctl_permit); /** Ioctl table */ static const struct drm_ioctl_desc drm_ioctls[] = { - DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, + DRM_UNLOCKED|DRM_RENDER_ALLOW|DRM_CONTROL_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0), DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0), DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY), diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index df0b61a60501..bd1a4156f647 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -77,6 +77,7 @@ config DRM_EXYNOS_VIDI config DRM_EXYNOS_G2D bool "Exynos DRM G2D" depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D + select FRAME_VECTOR help Choose this option if you want to use Exynos G2D for DRM. diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 535b4ad6c4b1..3734c34aed16 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -194,10 +194,8 @@ struct g2d_cmdlist_userptr { dma_addr_t dma_addr; unsigned long userptr; unsigned long size; - struct page **pages; - unsigned int npages; + struct frame_vector *vec; struct sg_table *sgt; - struct vm_area_struct *vma; atomic_t refcount; bool in_pool; bool out_of_list; @@ -367,6 +365,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev, { struct g2d_cmdlist_userptr *g2d_userptr = (struct g2d_cmdlist_userptr *)obj; + struct page **pages; if (!obj) return; @@ -386,19 +385,21 @@ out: exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt, DMA_BIDIRECTIONAL); - exynos_gem_put_pages_to_userptr(g2d_userptr->pages, - g2d_userptr->npages, - g2d_userptr->vma); + pages = frame_vector_pages(g2d_userptr->vec); + if (!IS_ERR(pages)) { + int i; - exynos_gem_put_vma(g2d_userptr->vma); + for (i = 0; i < frame_vector_count(g2d_userptr->vec); i++) + set_page_dirty_lock(pages[i]); + } + put_vaddr_frames(g2d_userptr->vec); + frame_vector_destroy(g2d_userptr->vec); if (!g2d_userptr->out_of_list) list_del_init(&g2d_userptr->list); sg_free_table(g2d_userptr->sgt); kfree(g2d_userptr->sgt); - - drm_free_large(g2d_userptr->pages); kfree(g2d_userptr); } @@ -412,9 +413,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; struct g2d_cmdlist_userptr *g2d_userptr; struct g2d_data *g2d; - struct page **pages; struct sg_table *sgt; - struct vm_area_struct *vma; unsigned long start, end; unsigned int npages, offset; int ret; @@ -460,65 +459,40 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, return ERR_PTR(-ENOMEM); atomic_set(&g2d_userptr->refcount, 1); + g2d_userptr->size = size; start = userptr & PAGE_MASK; offset = userptr & ~PAGE_MASK; end = PAGE_ALIGN(userptr + size); npages = (end - start) >> PAGE_SHIFT; - g2d_userptr->npages = npages; - - pages = drm_calloc_large(npages, sizeof(struct page *)); - if (!pages) { - DRM_ERROR("failed to allocate pages.\n"); + g2d_userptr->vec = frame_vector_create(npages); + if (!g2d_userptr->vec) { ret = -ENOMEM; goto err_free; } - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, userptr); - if (!vma) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to get vm region.\n"); + ret = get_vaddr_frames(start, npages, true, true, g2d_userptr->vec); + if (ret != npages) { + DRM_ERROR("failed to get user pages from userptr.\n"); + if (ret < 0) + goto err_destroy_framevec; ret = -EFAULT; - goto err_free_pages; + goto err_put_framevec; } - - if (vma->vm_end < userptr + size) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("vma is too small.\n"); + if (frame_vector_to_pages(g2d_userptr->vec) < 0) { ret = -EFAULT; - goto err_free_pages; - } - - g2d_userptr->vma = exynos_gem_get_vma(vma); - if (!g2d_userptr->vma) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to copy vma.\n"); - ret = -ENOMEM; - goto err_free_pages; - } - - g2d_userptr->size = size; - - ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK, - npages, pages, vma); - if (ret < 0) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to get user pages from userptr.\n"); - goto err_put_vma; + goto err_put_framevec; } - up_read(¤t->mm->mmap_sem); - g2d_userptr->pages = pages; - sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) { ret = -ENOMEM; - goto err_free_userptr; + goto err_put_framevec; } - ret = sg_alloc_table_from_pages(sgt, pages, npages, offset, - size, GFP_KERNEL); + ret = sg_alloc_table_from_pages(sgt, + frame_vector_pages(g2d_userptr->vec), + npages, offset, size, GFP_KERNEL); if (ret < 0) { DRM_ERROR("failed to get sgt from pages.\n"); goto err_free_sgt; @@ -553,16 +527,11 @@ err_sg_free_table: err_free_sgt: kfree(sgt); -err_free_userptr: - exynos_gem_put_pages_to_userptr(g2d_userptr->pages, - g2d_userptr->npages, - g2d_userptr->vma); - -err_put_vma: - exynos_gem_put_vma(g2d_userptr->vma); +err_put_framevec: + put_vaddr_frames(g2d_userptr->vec); -err_free_pages: - drm_free_large(pages); +err_destroy_framevec: + frame_vector_destroy(g2d_userptr->vec); err_free: kfree(g2d_userptr); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 62b9ea1b07fb..f12fbc36b120 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -366,103 +366,6 @@ int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, return 0; } -struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma) -{ - struct vm_area_struct *vma_copy; - - vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); - if (!vma_copy) - return NULL; - - if (vma->vm_ops && vma->vm_ops->open) - vma->vm_ops->open(vma); - - if (vma->vm_file) - get_file(vma->vm_file); - - memcpy(vma_copy, vma, sizeof(*vma)); - - vma_copy->vm_mm = NULL; - vma_copy->vm_next = NULL; - vma_copy->vm_prev = NULL; - - return vma_copy; -} - -void exynos_gem_put_vma(struct vm_area_struct *vma) -{ - if (!vma) - return; - - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - - if (vma->vm_file) - fput(vma->vm_file); - - kfree(vma); -} - -int exynos_gem_get_pages_from_userptr(unsigned long start, - unsigned int npages, - struct page **pages, - struct vm_area_struct *vma) -{ - int get_npages; - - /* the memory region mmaped with VM_PFNMAP. */ - if (vma_is_io(vma)) { - unsigned int i; - - for (i = 0; i < npages; ++i, start += PAGE_SIZE) { - unsigned long pfn; - int ret = follow_pfn(vma, start, &pfn); - if (ret) - return ret; - - pages[i] = pfn_to_page(pfn); - } - - if (i != npages) { - DRM_ERROR("failed to get user_pages.\n"); - return -EINVAL; - } - - return 0; - } - - get_npages = get_user_pages(current, current->mm, start, - npages, 1, 1, pages, NULL); - get_npages = max(get_npages, 0); - if (get_npages != npages) { - DRM_ERROR("failed to get user_pages.\n"); - while (get_npages) - put_page(pages[--get_npages]); - return -EFAULT; - } - - return 0; -} - -void exynos_gem_put_pages_to_userptr(struct page **pages, - unsigned int npages, - struct vm_area_struct *vma) -{ - if (!vma_is_io(vma)) { - unsigned int i; - - for (i = 0; i < npages; i++) { - set_page_dirty_lock(pages[i]); - - /* - * undo the reference we took when populating - * the table. - */ - put_page(pages[i]); - } - } -} - int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev, struct sg_table *sgt, enum dma_data_direction dir) diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c index 82be6b86a168..d1e300dcd544 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c @@ -58,7 +58,8 @@ static void fsl_dcu_drm_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) { struct fsl_dcu_drm_device *fsl_dev = plane->dev->dev_private; - unsigned int index, value, ret; + unsigned int value; + int index, ret; index = fsl_dcu_drm_plane_index(plane); if (index < 0) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 5a244ab9395b..39d73dbc1c47 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -640,6 +640,32 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3; /* + * On HSW, the DSL reg (0x70000) appears to return 0 if we + * read it just before the start of vblank. So try it again + * so we don't accidentally end up spanning a vblank frame + * increment, causing the pipe_update_end() code to squak at us. + * + * The nature of this problem means we can't simply check the ISR + * bit and return the vblank start value; nor can we use the scanline + * debug register in the transcoder as it appears to have the same + * problem. We may need to extend this to include other platforms, + * but so far testing only shows the problem on HSW. + */ + if (IS_HASWELL(dev) && !position) { + int i, temp; + + for (i = 0; i < 100; i++) { + udelay(1); + temp = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & + DSL_LINEMASK_GEN3; + if (temp != position) { + position = temp; + break; + } + } + } + + /* * See update_scanline_offset() for the details on the * scanline_offset adjustment. */ diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 89c1a8ce1f98..2a5c76faf9f8 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -430,7 +430,7 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder) /** * intel_audio_codec_disable - Disable the audio codec for HD audio - * @encoder: encoder on which to disable audio + * @intel_encoder: encoder on which to disable audio * * The disable sequences must be performed before disabling the transcoder or * port. diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index b3e437b3bb54..c19e669ffe50 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -42,7 +42,7 @@ find_section(const void *_bdb, int section_id) const struct bdb_header *bdb = _bdb; const u8 *base = _bdb; int index = 0; - u16 total, current_size; + u32 total, current_size; u8 current_id; /* skip to first section */ @@ -57,6 +57,10 @@ find_section(const void *_bdb, int section_id) current_size = *((const u16 *)(base + index)); index += 2; + /* The MIPI Sequence Block v3+ has a separate size field. */ + if (current_id == BDB_MIPI_SEQUENCE && *(base + index) >= 3) + current_size = *((const u32 *)(base + index + 1)); + if (index + current_size > total) return NULL; @@ -799,6 +803,12 @@ parse_mipi(struct drm_i915_private *dev_priv, const struct bdb_header *bdb) return; } + /* Fail gracefully for forward incompatible sequence block. */ + if (sequence->version >= 3) { + DRM_ERROR("Unable to parse MIPI Sequence Block v3+\n"); + return; + } + DRM_DEBUG_DRIVER("Found MIPI sequence block\n"); block_size = get_blocksize(sequence); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8cc9264f7809..cf418be7d30a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15087,9 +15087,12 @@ static void readout_plane_state(struct intel_crtc *crtc, plane_state = to_intel_plane_state(p->base.state); - if (p->base.type == DRM_PLANE_TYPE_PRIMARY) + if (p->base.type == DRM_PLANE_TYPE_PRIMARY) { plane_state->visible = primary_get_hw_state(crtc); - else { + if (plane_state->visible) + crtc->base.state->plane_mask |= + 1 << drm_plane_index(&p->base); + } else { if (active) p->disable_plane(&p->base, &crtc->base); diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 87de15ea1f93..b35b5b2db4ec 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -186,17 +186,19 @@ static int mgag200fb_create(struct drm_fb_helper *helper, sysram = vmalloc(size); if (!sysram) - return -ENOMEM; + goto err_sysram; info = drm_fb_helper_alloc_fbi(helper); - if (IS_ERR(info)) - return PTR_ERR(info); + if (IS_ERR(info)) { + ret = PTR_ERR(info); + goto err_alloc_fbi; + } info->par = mfbdev; ret = mgag200_framebuffer_init(dev, &mfbdev->mfb, &mode_cmd, gobj); if (ret) - return ret; + goto err_framebuffer_init; mfbdev->sysram = sysram; mfbdev->size = size; @@ -225,7 +227,17 @@ static int mgag200fb_create(struct drm_fb_helper *helper, DRM_DEBUG_KMS("allocated %dx%d\n", fb->width, fb->height); + return 0; + +err_framebuffer_init: + drm_fb_helper_release_fbi(helper); +err_alloc_fbi: + vfree(sysram); +err_sysram: + drm_gem_object_unreference_unlocked(gobj); + + return ret; } static int mga_fbdev_destroy(struct drm_device *dev, @@ -276,23 +288,26 @@ int mgag200_fbdev_init(struct mga_device *mdev) ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper, mdev->num_crtc, MGAG200FB_CONN_LIMIT); if (ret) - return ret; + goto err_fb_helper; ret = drm_fb_helper_single_add_all_connectors(&mfbdev->helper); if (ret) - goto fini; + goto err_fb_setup; /* disable all the possible outputs/crtcs before entering KMS mode */ drm_helper_disable_unused_functions(mdev->dev); ret = drm_fb_helper_initial_config(&mfbdev->helper, bpp_sel); if (ret) - goto fini; + goto err_fb_setup; return 0; -fini: +err_fb_setup: drm_fb_helper_fini(&mfbdev->helper); +err_fb_helper: + mdev->mfbdev = NULL; + return ret; } diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index de06388069e7..b1a0f5656175 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -220,7 +220,7 @@ int mgag200_driver_load(struct drm_device *dev, unsigned long flags) } r = mgag200_mm_init(mdev); if (r) - goto out; + goto err_mm; drm_mode_config_init(dev); dev->mode_config.funcs = (void *)&mga_mode_funcs; @@ -233,7 +233,7 @@ int mgag200_driver_load(struct drm_device *dev, unsigned long flags) r = mgag200_modeset_init(mdev); if (r) { dev_err(&dev->pdev->dev, "Fatal error during modeset init: %d\n", r); - goto out; + goto err_modeset; } /* Make small buffers to store a hardware cursor (double buffered icon updates) */ @@ -241,20 +241,24 @@ int mgag200_driver_load(struct drm_device *dev, unsigned long flags) &mdev->cursor.pixels_1); mgag200_bo_create(dev, roundup(48*64, PAGE_SIZE), 0, 0, &mdev->cursor.pixels_2); - if (!mdev->cursor.pixels_2 || !mdev->cursor.pixels_1) - goto cursor_nospace; - mdev->cursor.pixels_current = mdev->cursor.pixels_1; - mdev->cursor.pixels_prev = mdev->cursor.pixels_2; - goto cursor_done; - cursor_nospace: - mdev->cursor.pixels_1 = NULL; - mdev->cursor.pixels_2 = NULL; - dev_warn(&dev->pdev->dev, "Could not allocate space for cursors. Not doing hardware cursors.\n"); - cursor_done: - -out: - if (r) - mgag200_driver_unload(dev); + if (!mdev->cursor.pixels_2 || !mdev->cursor.pixels_1) { + mdev->cursor.pixels_1 = NULL; + mdev->cursor.pixels_2 = NULL; + dev_warn(&dev->pdev->dev, + "Could not allocate space for cursors. Not doing hardware cursors.\n"); + } else { + mdev->cursor.pixels_current = mdev->cursor.pixels_1; + mdev->cursor.pixels_prev = mdev->cursor.pixels_2; + } + + return 0; + +err_modeset: + drm_mode_config_cleanup(dev); + mgag200_mm_fini(mdev); +err_mm: + dev->dev_private = NULL; + return r; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c index b1f73bee1368..b0d4b53b97f4 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c @@ -178,7 +178,6 @@ static int mdp5_hw_irqdomain_map(struct irq_domain *d, irq_set_chip_and_handler(irq, &mdp5_hw_irq_chip, handle_level_irq); irq_set_chip_data(irq, mdp5_kms); - set_irq_flags(irq, IRQF_VALID); return 0; } diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 7c6225c84ba6..dd845f82cc24 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -886,13 +886,15 @@ static enum drm_connector_status qxl_conn_detect( drm_connector_to_qxl_output(connector); struct drm_device *ddev = connector->dev; struct qxl_device *qdev = ddev->dev_private; - int connected; + bool connected = false; /* The first monitor is always connected */ - connected = (output->index == 0) || - (qdev->client_monitors_config && - qdev->client_monitors_config->count > output->index && - qxl_head_enabled(&qdev->client_monitors_config->heads[output->index])); + if (!qdev->client_monitors_config) { + if (output->index == 0) + connected = true; + } else + connected = qdev->client_monitors_config->count > output->index && + qxl_head_enabled(&qdev->client_monitors_config->heads[output->index]); DRM_DEBUG("#%d connected: %d\n", output->index, connected); if (!connected) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index d8319dae8358..f3f562f6d848 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1573,10 +1573,12 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon) drm_kms_helper_poll_disable(dev); + drm_modeset_lock_all(dev); /* turn off display hw */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); } + drm_modeset_unlock_all(dev); /* unpin the front buffers and cursors */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -1734,9 +1736,11 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon) if (fbcon) { drm_helper_resume_force_mode(dev); /* turn on display hw */ + drm_modeset_lock_all(dev); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); } + drm_modeset_unlock_all(dev); } drm_kms_helper_poll_enable(dev); diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 787cd8fd897f..e9115d3f67b0 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -2927,6 +2927,7 @@ static struct si_dpm_quirk si_dpm_quirk_list[] = { { PCI_VENDOR_ID_ATI, 0x6810, 0x1462, 0x3036, 0, 120000 }, { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0xe271, 0, 120000 }, { PCI_VENDOR_ID_ATI, 0x6810, 0x174b, 0xe271, 85000, 90000 }, + { PCI_VENDOR_ID_ATI, 0x6811, 0x1762, 0x2015, 0, 120000 }, { 0, 0, 0, 0 }, }; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 8d9b7de25613..745e996d2dbc 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -882,6 +882,8 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, if (ret) return ret; man = &bdev->man[mem_type]; + if (!man->has_type || !man->use_type) + continue; type_ok = ttm_bo_mt_compatible(man, mem_type, place, &cur_flags); @@ -889,6 +891,7 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, if (!type_ok) continue; + type_found = true; cur_flags = ttm_bo_select_caching(man, bo->mem.placement, cur_flags); /* @@ -901,12 +904,10 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, if (mem_type == TTM_PL_SYSTEM) break; - if (man->has_type && man->use_type) { - type_found = true; - ret = (*man->func->get_node)(man, bo, place, mem); - if (unlikely(ret)) - return ret; - } + ret = (*man->func->get_node)(man, bo, place, mem); + if (unlikely(ret)) + return ret; + if (mem->mm_node) break; } @@ -917,9 +918,6 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, return 0; } - if (!type_found) - return -EINVAL; - for (i = 0; i < placement->num_busy_placement; ++i) { const struct ttm_place *place = &placement->busy_placement[i]; @@ -927,11 +925,12 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, if (ret) return ret; man = &bdev->man[mem_type]; - if (!man->has_type) + if (!man->has_type || !man->use_type) continue; if (!ttm_bo_mt_compatible(man, mem_type, place, &cur_flags)) continue; + type_found = true; cur_flags = ttm_bo_select_caching(man, bo->mem.placement, cur_flags); /* @@ -957,8 +956,13 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, if (ret == -ERESTARTSYS) has_erestartsys = true; } - ret = (has_erestartsys) ? -ERESTARTSYS : -ENOMEM; - return ret; + + if (!type_found) { + printk(KERN_ERR TTM_PFX "No compatible memory type found.\n"); + return -EINVAL; + } + + return (has_erestartsys) ? -ERESTARTSYS : -ENOMEM; } EXPORT_SYMBOL(ttm_bo_mem_space); diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig index 67720f70fe29..b49445df8a7e 100644 --- a/drivers/gpu/drm/vmwgfx/Kconfig +++ b/drivers/gpu/drm/vmwgfx/Kconfig @@ -1,6 +1,6 @@ config DRM_VMWGFX tristate "DRM driver for VMware Virtual GPU" - depends on DRM && PCI + depends on DRM && PCI && X86 select FB_DEFERRED_IO select FB_CFB_FILLRECT select FB_CFB_COPYAREA diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c index ce659a125f2b..092ea81eeff7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c @@ -311,7 +311,6 @@ static int vmw_cotable_unbind(struct vmw_resource *res, struct vmw_private *dev_priv = res->dev_priv; struct ttm_buffer_object *bo = val_buf->bo; struct vmw_fence_obj *fence; - int ret; if (list_empty(&res->mob_head)) return 0; @@ -328,7 +327,7 @@ static int vmw_cotable_unbind(struct vmw_resource *res, if (likely(fence != NULL)) vmw_fence_obj_unreference(&fence); - return ret; + return 0; } /** diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index e13b20bd9908..2c7a25c71af2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -752,12 +752,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM); dev_priv->active_master = &dev_priv->fbdev_master; - - dev_priv->mmio_mtrr = arch_phys_wc_add(dev_priv->mmio_start, - dev_priv->mmio_size); - - dev_priv->mmio_virt = ioremap_wc(dev_priv->mmio_start, - dev_priv->mmio_size); + dev_priv->mmio_virt = ioremap_cache(dev_priv->mmio_start, + dev_priv->mmio_size); if (unlikely(dev_priv->mmio_virt == NULL)) { ret = -ENOMEM; @@ -913,7 +909,6 @@ out_no_device: out_err4: iounmap(dev_priv->mmio_virt); out_err3: - arch_phys_wc_del(dev_priv->mmio_mtrr); vmw_ttm_global_release(dev_priv); out_err0: for (i = vmw_res_context; i < vmw_res_max; ++i) @@ -964,7 +959,6 @@ static int vmw_driver_unload(struct drm_device *dev) ttm_object_device_release(&dev_priv->tdev); iounmap(dev_priv->mmio_virt); - arch_phys_wc_del(dev_priv->mmio_mtrr); if (dev_priv->ctx.staged_bindings) vmw_binding_state_free(dev_priv->ctx.staged_bindings); vmw_ttm_global_release(dev_priv); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 6d02de6dc36c..f19fd39b43e1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -376,7 +376,6 @@ struct vmw_private { uint32_t initial_width; uint32_t initial_height; u32 __iomem *mmio_virt; - int mmio_mtrr; uint32_t capabilities; uint32_t max_gmr_ids; uint32_t max_gmr_pages; @@ -631,7 +630,8 @@ extern int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv, uint32_t size, bool shareable, uint32_t *handle, - struct vmw_dma_buffer **p_dma_buf); + struct vmw_dma_buffer **p_dma_buf, + struct ttm_base_object **p_base); extern int vmw_user_dmabuf_reference(struct ttm_object_file *tfile, struct vmw_dma_buffer *dma_buf, uint32_t *handle); @@ -645,7 +645,8 @@ extern uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo, uint32_t cur_validate_node); extern void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo); extern int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, - uint32_t id, struct vmw_dma_buffer **out); + uint32_t id, struct vmw_dma_buffer **out, + struct ttm_base_object **base); extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index b56565457c96..5da5de0cb522 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1236,7 +1236,8 @@ static int vmw_translate_mob_ptr(struct vmw_private *dev_priv, struct vmw_relocation *reloc; int ret; - ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo); + ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo, + NULL); if (unlikely(ret != 0)) { DRM_ERROR("Could not find or use MOB buffer.\n"); ret = -EINVAL; @@ -1296,7 +1297,8 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv, struct vmw_relocation *reloc; int ret; - ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo); + ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo, + NULL); if (unlikely(ret != 0)) { DRM_ERROR("Could not find or use GMR region.\n"); ret = -EINVAL; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 61fb7f3de311..15a6c01cd016 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1685,7 +1685,6 @@ int vmw_kms_helper_dirty(struct vmw_private *dev_priv, struct drm_crtc *crtc; u32 num_units = 0; u32 i, k; - int ret; dirty->dev_priv = dev_priv; @@ -1711,7 +1710,7 @@ int vmw_kms_helper_dirty(struct vmw_private *dev_priv, if (!dirty->cmd) { DRM_ERROR("Couldn't reserve fifo space " "for dirty blits.\n"); - return ret; + return -ENOMEM; } memset(dirty->cmd, 0, dirty->fifo_reserve_size); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c index 76069f093ccf..222c9c2123a1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c @@ -484,7 +484,7 @@ int vmw_overlay_ioctl(struct drm_device *dev, void *data, goto out_unlock; } - ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &buf); + ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &buf, NULL); if (ret) goto out_unlock; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index c1912f852b42..e57667ca7557 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -354,7 +354,7 @@ int vmw_user_lookup_handle(struct vmw_private *dev_priv, } *out_surf = NULL; - ret = vmw_user_dmabuf_lookup(tfile, handle, out_buf); + ret = vmw_user_dmabuf_lookup(tfile, handle, out_buf, NULL); return ret; } @@ -481,7 +481,8 @@ int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv, uint32_t size, bool shareable, uint32_t *handle, - struct vmw_dma_buffer **p_dma_buf) + struct vmw_dma_buffer **p_dma_buf, + struct ttm_base_object **p_base) { struct vmw_user_dma_buffer *user_bo; struct ttm_buffer_object *tmp; @@ -515,6 +516,10 @@ int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv, } *p_dma_buf = &user_bo->dma; + if (p_base) { + *p_base = &user_bo->prime.base; + kref_get(&(*p_base)->refcount); + } *handle = user_bo->prime.base.hash.key; out_no_base_object: @@ -631,6 +636,7 @@ int vmw_user_dmabuf_synccpu_ioctl(struct drm_device *dev, void *data, struct vmw_dma_buffer *dma_buf; struct vmw_user_dma_buffer *user_bo; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct ttm_base_object *buffer_base; int ret; if ((arg->flags & (drm_vmw_synccpu_read | drm_vmw_synccpu_write)) == 0 @@ -643,7 +649,8 @@ int vmw_user_dmabuf_synccpu_ioctl(struct drm_device *dev, void *data, switch (arg->op) { case drm_vmw_synccpu_grab: - ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &dma_buf); + ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &dma_buf, + &buffer_base); if (unlikely(ret != 0)) return ret; @@ -651,6 +658,7 @@ int vmw_user_dmabuf_synccpu_ioctl(struct drm_device *dev, void *data, dma); ret = vmw_user_dmabuf_synccpu_grab(user_bo, tfile, arg->flags); vmw_dmabuf_unreference(&dma_buf); + ttm_base_object_unref(&buffer_base); if (unlikely(ret != 0 && ret != -ERESTARTSYS && ret != -EBUSY)) { DRM_ERROR("Failed synccpu grab on handle 0x%08x.\n", @@ -692,7 +700,8 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data, return ret; ret = vmw_user_dmabuf_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, - req->size, false, &handle, &dma_buf); + req->size, false, &handle, &dma_buf, + NULL); if (unlikely(ret != 0)) goto out_no_dmabuf; @@ -721,7 +730,8 @@ int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data, } int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, - uint32_t handle, struct vmw_dma_buffer **out) + uint32_t handle, struct vmw_dma_buffer **out, + struct ttm_base_object **p_base) { struct vmw_user_dma_buffer *vmw_user_bo; struct ttm_base_object *base; @@ -743,7 +753,10 @@ int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, prime.base); (void)ttm_bo_reference(&vmw_user_bo->dma.base); - ttm_base_object_unref(&base); + if (p_base) + *p_base = base; + else + ttm_base_object_unref(&base); *out = &vmw_user_bo->dma; return 0; @@ -1004,7 +1017,7 @@ int vmw_dumb_create(struct drm_file *file_priv, ret = vmw_user_dmabuf_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, args->size, false, &args->handle, - &dma_buf); + &dma_buf, NULL); if (unlikely(ret != 0)) goto out_no_dmabuf; @@ -1032,7 +1045,7 @@ int vmw_dumb_map_offset(struct drm_file *file_priv, struct vmw_dma_buffer *out_buf; int ret; - ret = vmw_user_dmabuf_lookup(tfile, handle, &out_buf); + ret = vmw_user_dmabuf_lookup(tfile, handle, &out_buf, NULL); if (ret != 0) return -EINVAL; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c index bba1ee395478..fd47547b0234 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c @@ -855,7 +855,7 @@ static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv, if (buffer_handle != SVGA3D_INVALID_ID) { ret = vmw_user_dmabuf_lookup(tfile, buffer_handle, - &buffer); + &buffer, NULL); if (unlikely(ret != 0)) { DRM_ERROR("Could not find buffer for shader " "creation.\n"); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 3361769842f4..64b50409fa07 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -46,6 +46,7 @@ struct vmw_user_surface { struct vmw_surface srf; uint32_t size; struct drm_master *master; + struct ttm_base_object *backup_base; }; /** @@ -656,6 +657,7 @@ static void vmw_user_surface_base_release(struct ttm_base_object **p_base) struct vmw_resource *res = &user_srf->srf.res; *p_base = NULL; + ttm_base_object_unref(&user_srf->backup_base); vmw_resource_unreference(&res); } @@ -851,7 +853,8 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, res->backup_size, true, &backup_handle, - &res->backup); + &res->backup, + &user_srf->backup_base); if (unlikely(ret != 0)) { vmw_resource_unreference(&res); goto out_unlock; @@ -1321,7 +1324,8 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data, if (req->buffer_handle != SVGA3D_INVALID_ID) { ret = vmw_user_dmabuf_lookup(tfile, req->buffer_handle, - &res->backup); + &res->backup, + &user_srf->backup_base); if (ret == 0 && res->backup->base.num_pages * PAGE_SIZE < res->backup_size) { DRM_ERROR("Surface backup buffer is too small.\n"); @@ -1335,7 +1339,8 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data, req->drm_surface_flags & drm_vmw_surface_flag_shareable, &backup_handle, - &res->backup); + &res->backup, + &user_srf->backup_base); if (unlikely(ret != 0)) { vmw_resource_unreference(&res); diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index 243f99a80253..e5a38d202a21 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -912,7 +912,7 @@ static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) } } -static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc) +static void ipu_irq_handler(struct irq_desc *desc) { struct ipu_soc *ipu = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); @@ -925,7 +925,7 @@ static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } -static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc) +static void ipu_err_irq_handler(struct irq_desc *desc) { struct ipu_soc *ipu = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); @@ -1099,8 +1099,7 @@ static int ipu_irq_init(struct ipu_soc *ipu) } ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", - handle_level_irq, 0, - IRQF_VALID, 0); + handle_level_irq, 0, 0, 0); if (ret < 0) { dev_err(ipu->dev, "failed to alloc generic irq chips\n"); irq_domain_remove(ipu->domain); diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 2f9aead4ecfc..652afd11a9ef 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -204,6 +204,8 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) spin_lock_irqsave(&vmbus_connection.channel_lock, flags); list_del(&channel->listentry); spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + + primary_channel = channel; } else { primary_channel = channel->primary_channel; spin_lock_irqsave(&primary_channel->lock, flags); @@ -211,6 +213,14 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) primary_channel->num_sc--; spin_unlock_irqrestore(&primary_channel->lock, flags); } + + /* + * We need to free the bit for init_vp_index() to work in the case + * of sub-channel, when we reload drivers like hv_netvsc. + */ + cpumask_clear_cpu(channel->target_cpu, + &primary_channel->alloced_cpus_in_node); + free_channel(channel); } @@ -458,6 +468,13 @@ static void init_vp_index(struct vmbus_channel *channel, const uuid_le *type_gui continue; } + /* + * NOTE: in the case of sub-channel, we clear the sub-channel + * related bit(s) in primary->alloced_cpus_in_node in + * hv_process_channel_removal(), so when we reload drivers + * like hv_netvsc in SMP guest, here we're able to re-allocate + * bit from primary->alloced_cpus_in_node. + */ if (!cpumask_test_cpu(cur_cpu, &primary->alloced_cpus_in_node)) { cpumask_set_cpu(cur_cpu, diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 500b262b89bb..e13c902e8966 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1140,8 +1140,8 @@ config SENSORS_NCT6775 help If you say yes here you get support for the hardware monitoring functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D, - NCT6791D, NCT6792D and compatible Super-I/O chips. This driver - replaces the w83627ehf driver for NCT6775F and NCT6776F. + NCT6791D, NCT6792D, NCT6793D, and compatible Super-I/O chips. This + driver replaces the w83627ehf driver for NCT6775F and NCT6776F. This driver can also be built as a module. If so, the module will be called nct6775. diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c index 6cb89c0ebab6..1fd46859ed29 100644 --- a/drivers/hwmon/abx500.c +++ b/drivers/hwmon/abx500.c @@ -470,6 +470,7 @@ static const struct of_device_id abx500_temp_match[] = { { .compatible = "stericsson,abx500-temp" }, {}, }; +MODULE_DEVICE_TABLE(of, abx500_temp_match); #endif static struct platform_driver abx500_temp_driver = { diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index a3dae6d0082a..82de3deeb18a 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -539,6 +539,7 @@ static const struct of_device_id of_gpio_fan_match[] = { { .compatible = "gpio-fan", }, {}, }; +MODULE_DEVICE_TABLE(of, of_gpio_fan_match); #endif /* CONFIG_OF_GPIO */ static int gpio_fan_probe(struct platform_device *pdev) diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index fe41d5ae7cb2..e4e57bbafb10 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -104,7 +104,7 @@ static inline long lm75_reg_to_mc(s16 temp, u8 resolution) /* sysfs attributes for hwmon */ -static int lm75_read_temp(void *dev, long *temp) +static int lm75_read_temp(void *dev, int *temp) { struct lm75_data *data = lm75_update_device(dev); diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index bd1c99deac71..8b4fa55e46c6 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -39,6 +39,7 @@ * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 * nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3 * nct6792d 15 6 6 2+6 0xc910 0xc1 0x5ca3 + * nct6793d 15 6 6 2+6 0xd120 0xc1 0x5ca3 * * #temp lists the number of monitored temperature sources (first value) plus * the number of directly connectable temperature sensors (second value). @@ -63,7 +64,7 @@ #define USE_ALTERNATE -enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792 }; +enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793 }; /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { @@ -73,6 +74,17 @@ static const char * const nct6775_device_names[] = { "nct6779", "nct6791", "nct6792", + "nct6793", +}; + +static const char * const nct6775_sio_names[] __initconst = { + "NCT6106D", + "NCT6775F", + "NCT6776D/F", + "NCT6779D", + "NCT6791D", + "NCT6792D", + "NCT6793D", }; static unsigned short force_id; @@ -104,6 +116,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_NCT6779_ID 0xc560 #define SIO_NCT6791_ID 0xc800 #define SIO_NCT6792_ID 0xc910 +#define SIO_NCT6793_ID 0xd120 #define SIO_ID_MASK 0xFFF0 enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; @@ -354,6 +367,10 @@ static const u16 NCT6775_REG_TEMP_CRIT[ARRAY_SIZE(nct6775_temp_label) - 1] /* NCT6776 specific data */ +/* STEP_UP_TIME and STEP_DOWN_TIME regs are swapped for all chips but NCT6775 */ +#define NCT6776_REG_FAN_STEP_UP_TIME NCT6775_REG_FAN_STEP_DOWN_TIME +#define NCT6776_REG_FAN_STEP_DOWN_TIME NCT6775_REG_FAN_STEP_UP_TIME + static const s8 NCT6776_ALARM_BITS[] = { 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ @@ -533,7 +550,7 @@ static const s8 NCT6791_ALARM_BITS[] = { 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ 12, 9 }; /* intrusion0, intrusion1 */ -/* NCT6792 specific data */ +/* NCT6792/NCT6793 specific data */ static const u16 NCT6792_REG_TEMP_MON[] = { 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d }; @@ -1056,6 +1073,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) case nct6779: case nct6791: case nct6792: + case nct6793: return reg == 0x150 || reg == 0x153 || reg == 0x155 || ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) || reg == 0x402 || @@ -1407,6 +1425,7 @@ static void nct6775_update_pwm_limits(struct device *dev) case nct6779: case nct6791: case nct6792: + case nct6793: reg = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i]); if (reg & data->CRITICAL_PWM_ENABLE_MASK) @@ -2822,6 +2841,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, case nct6779: case nct6791: case nct6792: + case nct6793: nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val); reg = nct6775_read_value(data, @@ -3256,7 +3276,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm4pin = false; pwm5pin = false; pwm6pin = false; - } else { /* NCT6779D, NCT6791D, or NCT6792D */ + } else { /* NCT6779D, NCT6791D, NCT6792D, or NCT6793D */ regval = superio_inb(sioreg, 0x1c); fan3pin = !(regval & (1 << 5)); @@ -3269,7 +3289,8 @@ nct6775_check_fan_inputs(struct nct6775_data *data) fan4min = fan4pin; - if (data->kind == nct6791 || data->kind == nct6792) { + if (data->kind == nct6791 || data->kind == nct6792 || + data->kind == nct6793) { regval = superio_inb(sioreg, 0x2d); fan6pin = (regval & (1 << 1)); pwm6pin = (regval & (1 << 0)); @@ -3528,8 +3549,8 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES; data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; - data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; - data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME; data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; data->REG_PWM[0] = NCT6775_REG_PWM; data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; @@ -3600,8 +3621,8 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; - data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; - data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME; data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; data->REG_PWM[0] = NCT6775_REG_PWM; data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; @@ -3643,6 +3664,7 @@ static int nct6775_probe(struct platform_device *pdev) break; case nct6791: case nct6792: + case nct6793: data->in_num = 15; data->pwm_num = 6; data->auto_pwm_num = 4; @@ -3677,8 +3699,8 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; - data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; - data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME; data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; data->REG_PWM[0] = NCT6775_REG_PWM; data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; @@ -3918,6 +3940,7 @@ static int nct6775_probe(struct platform_device *pdev) case nct6779: case nct6791: case nct6792: + case nct6793: break; } @@ -3950,6 +3973,7 @@ static int nct6775_probe(struct platform_device *pdev) break; case nct6791: case nct6792: + case nct6793: tmp |= 0x7e; break; } @@ -4047,7 +4071,8 @@ static int __maybe_unused nct6775_resume(struct device *dev) if (reg != data->sio_reg_enable) superio_outb(sioreg, SIO_REG_ENABLE, data->sio_reg_enable); - if (data->kind == nct6791 || data->kind == nct6792) + if (data->kind == nct6791 || data->kind == nct6792 || + data->kind == nct6793) nct6791_enable_io_mapping(sioreg); superio_exit(sioreg); @@ -4106,15 +4131,6 @@ static struct platform_driver nct6775_driver = { .probe = nct6775_probe, }; -static const char * const nct6775_sio_names[] __initconst = { - "NCT6106D", - "NCT6775F", - "NCT6776D/F", - "NCT6779D", - "NCT6791D", - "NCT6792D", -}; - /* nct6775_find() looks for a '627 in the Super-I/O config space */ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) { @@ -4150,6 +4166,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) case SIO_NCT6792_ID: sio_data->kind = nct6792; break; + case SIO_NCT6793_ID: + sio_data->kind = nct6793; + break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); @@ -4175,7 +4194,8 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); } - if (sio_data->kind == nct6791 || sio_data->kind == nct6792) + if (sio_data->kind == nct6791 || sio_data->kind == nct6792 || + sio_data->kind == nct6793) nct6791_enable_io_mapping(sioaddr); superio_exit(sioaddr); @@ -4285,7 +4305,7 @@ static void __exit sensors_nct6775_exit(void) } MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); -MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D/NCT6792D driver"); +MODULE_DESCRIPTION("Driver for NCT6775F and compatible chips"); MODULE_LICENSE("GPL"); module_init(sensors_nct6775_init); diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index dc0b76c5e302..feed30646d91 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -477,7 +477,7 @@ static int ntc_thermistor_get_ohm(struct ntc_data *data) return -EINVAL; } -static int ntc_read_temp(void *dev, long *temp) +static int ntc_read_temp(void *dev, int *temp) { struct ntc_data *data = dev_get_drvdata(dev); int ohm; diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 2d9a712699ff..3e23003f78b0 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -323,6 +323,7 @@ static const struct of_device_id of_pwm_fan_match[] = { { .compatible = "pwm-fan", }, {}, }; +MODULE_DEVICE_TABLE(of, of_pwm_fan_match); static struct platform_driver pwm_fan_driver = { .probe = pwm_fan_probe, diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index 9da2735f1424..65482624ea2c 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -98,7 +98,7 @@ static struct tmp102 *tmp102_update_device(struct device *dev) return tmp102; } -static int tmp102_read_temp(void *dev, long *temp) +static int tmp102_read_temp(void *dev, int *temp) { struct tmp102 *tmp102 = tmp102_update_device(dev); diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 3a3738fe016b..cd4510a63375 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -620,7 +620,7 @@ static struct cpuidle_state skl_cstates[] = { .name = "C6-SKL", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, - .exit_latency = 75, + .exit_latency = 85, .target_residency = 200, .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, @@ -636,11 +636,19 @@ static struct cpuidle_state skl_cstates[] = { .name = "C8-SKL", .desc = "MWAIT 0x40", .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TLB_FLUSHED, - .exit_latency = 174, + .exit_latency = 200, .target_residency = 800, .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { + .name = "C9-SKL", + .desc = "MWAIT 0x50", + .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 480, + .target_residency = 5000, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { .name = "C10-SKL", .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index da4c6979fbb8..aa26f3c3416b 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -56,7 +56,6 @@ config INFINIBAND_ADDR_TRANS source "drivers/infiniband/hw/mthca/Kconfig" source "drivers/infiniband/hw/qib/Kconfig" -source "drivers/infiniband/hw/ehca/Kconfig" source "drivers/infiniband/hw/cxgb3/Kconfig" source "drivers/infiniband/hw/cxgb4/Kconfig" source "drivers/infiniband/hw/mlx4/Kconfig" diff --git a/drivers/infiniband/hw/Makefile b/drivers/infiniband/hw/Makefile index 1bdb9996d371..aded2a5cc2d5 100644 --- a/drivers/infiniband/hw/Makefile +++ b/drivers/infiniband/hw/Makefile @@ -1,6 +1,5 @@ obj-$(CONFIG_INFINIBAND_MTHCA) += mthca/ obj-$(CONFIG_INFINIBAND_QIB) += qib/ -obj-$(CONFIG_INFINIBAND_EHCA) += ehca/ obj-$(CONFIG_INFINIBAND_CXGB3) += cxgb3/ obj-$(CONFIG_INFINIBAND_CXGB4) += cxgb4/ obj-$(CONFIG_MLX4_INFINIBAND) += mlx4/ diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 41d6911e244e..f1ccd40beae9 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -245,7 +245,6 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, props->device_cap_flags |= IB_DEVICE_BAD_QKEY_CNTR; if (MLX5_CAP_GEN(mdev, apm)) props->device_cap_flags |= IB_DEVICE_AUTO_PATH_MIG; - props->device_cap_flags |= IB_DEVICE_LOCAL_DMA_LKEY; if (MLX5_CAP_GEN(mdev, xrc)) props->device_cap_flags |= IB_DEVICE_XRC; props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS; @@ -795,53 +794,6 @@ static int mlx5_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vm return 0; } -static int alloc_pa_mkey(struct mlx5_ib_dev *dev, u32 *key, u32 pdn) -{ - struct mlx5_create_mkey_mbox_in *in; - struct mlx5_mkey_seg *seg; - struct mlx5_core_mr mr; - int err; - - in = kzalloc(sizeof(*in), GFP_KERNEL); - if (!in) - return -ENOMEM; - - seg = &in->seg; - seg->flags = MLX5_PERM_LOCAL_READ | MLX5_ACCESS_MODE_PA; - seg->flags_pd = cpu_to_be32(pdn | MLX5_MKEY_LEN64); - seg->qpn_mkey7_0 = cpu_to_be32(0xffffff << 8); - seg->start_addr = 0; - - err = mlx5_core_create_mkey(dev->mdev, &mr, in, sizeof(*in), - NULL, NULL, NULL); - if (err) { - mlx5_ib_warn(dev, "failed to create mkey, %d\n", err); - goto err_in; - } - - kfree(in); - *key = mr.key; - - return 0; - -err_in: - kfree(in); - - return err; -} - -static void free_pa_mkey(struct mlx5_ib_dev *dev, u32 key) -{ - struct mlx5_core_mr mr; - int err; - - memset(&mr, 0, sizeof(mr)); - mr.key = key; - err = mlx5_core_destroy_mkey(dev->mdev, &mr); - if (err) - mlx5_ib_warn(dev, "failed to destroy mkey 0x%x\n", key); -} - static struct ib_pd *mlx5_ib_alloc_pd(struct ib_device *ibdev, struct ib_ucontext *context, struct ib_udata *udata) @@ -867,13 +819,6 @@ static struct ib_pd *mlx5_ib_alloc_pd(struct ib_device *ibdev, kfree(pd); return ERR_PTR(-EFAULT); } - } else { - err = alloc_pa_mkey(to_mdev(ibdev), &pd->pa_lkey, pd->pdn); - if (err) { - mlx5_core_dealloc_pd(to_mdev(ibdev)->mdev, pd->pdn); - kfree(pd); - return ERR_PTR(err); - } } return &pd->ibpd; @@ -884,9 +829,6 @@ static int mlx5_ib_dealloc_pd(struct ib_pd *pd) struct mlx5_ib_dev *mdev = to_mdev(pd->device); struct mlx5_ib_pd *mpd = to_mpd(pd); - if (!pd->uobject) - free_pa_mkey(mdev, mpd->pa_lkey); - mlx5_core_dealloc_pd(mdev->mdev, mpd->pdn); kfree(mpd); @@ -1245,18 +1187,10 @@ static int create_dev_resources(struct mlx5_ib_resources *devr) struct ib_srq_init_attr attr; struct mlx5_ib_dev *dev; struct ib_cq_init_attr cq_attr = {.cqe = 1}; - u32 rsvd_lkey; int ret = 0; dev = container_of(devr, struct mlx5_ib_dev, devr); - ret = mlx5_core_query_special_context(dev->mdev, &rsvd_lkey); - if (ret) { - pr_err("Failed to query special context %d\n", ret); - return ret; - } - dev->ib_dev.local_dma_lkey = rsvd_lkey; - devr->p0 = mlx5_ib_alloc_pd(&dev->ib_dev, NULL, NULL); if (IS_ERR(devr->p0)) { ret = PTR_ERR(devr->p0); @@ -1418,6 +1352,7 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) strlcpy(dev->ib_dev.name, "mlx5_%d", IB_DEVICE_NAME_MAX); dev->ib_dev.owner = THIS_MODULE; dev->ib_dev.node_type = RDMA_NODE_IB_CA; + dev->ib_dev.local_dma_lkey = 0 /* not supported for now */; dev->num_ports = MLX5_CAP_GEN(mdev, num_ports); dev->ib_dev.phys_port_cnt = dev->num_ports; dev->ib_dev.num_comp_vectors = diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index bb8cda79e881..22123b79d550 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -103,7 +103,6 @@ static inline struct mlx5_ib_ucontext *to_mucontext(struct ib_ucontext *ibuconte struct mlx5_ib_pd { struct ib_pd ibpd; u32 pdn; - u32 pa_lkey; }; /* Use macros here so that don't have to duplicate @@ -213,7 +212,6 @@ struct mlx5_ib_qp { int uuarn; int create_type; - u32 pa_lkey; /* Store signature errors */ bool signature_en; diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index c745c6c5e10d..6f521a3418e8 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -925,8 +925,6 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd, err = create_kernel_qp(dev, init_attr, qp, &in, &inlen); if (err) mlx5_ib_dbg(dev, "err %d\n", err); - else - qp->pa_lkey = to_mpd(pd)->pa_lkey; } if (err) @@ -2045,7 +2043,7 @@ static void set_frwr_pages(struct mlx5_wqe_data_seg *dseg, mfrpl->mapped_page_list[i] = cpu_to_be64(page_list[i] | perm); dseg->addr = cpu_to_be64(mfrpl->map); dseg->byte_count = cpu_to_be32(ALIGN(sizeof(u64) * wr->wr.fast_reg.page_list_len, 64)); - dseg->lkey = cpu_to_be32(pd->pa_lkey); + dseg->lkey = cpu_to_be32(pd->ibpd.local_dma_lkey); } static __be32 send_ieth(struct ib_send_wr *wr) diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index ca2873698d75..4cd5428a2399 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -80,7 +80,7 @@ enum { IPOIB_NUM_WC = 4, IPOIB_MAX_PATH_REC_QUEUE = 3, - IPOIB_MAX_MCAST_QUEUE = 3, + IPOIB_MAX_MCAST_QUEUE = 64, IPOIB_FLAG_OPER_UP = 0, IPOIB_FLAG_INITIALIZED = 1, @@ -548,6 +548,8 @@ void ipoib_path_iter_read(struct ipoib_path_iter *iter, int ipoib_mcast_attach(struct net_device *dev, u16 mlid, union ib_gid *mgid, int set_qkey); +int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast); +struct ipoib_mcast *__ipoib_mcast_find(struct net_device *dev, void *mgid); int ipoib_init_qp(struct net_device *dev); int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 36536ce5a3e2..f74316e679d2 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1149,6 +1149,9 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv) unsigned long dt; unsigned long flags; int i; + LIST_HEAD(remove_list); + struct ipoib_mcast *mcast, *tmcast; + struct net_device *dev = priv->dev; if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags)) return; @@ -1176,6 +1179,19 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv) lockdep_is_held(&priv->lock))) != NULL) { /* was the neigh idle for two GC periods */ if (time_after(neigh_obsolete, neigh->alive)) { + u8 *mgid = neigh->daddr + 4; + + /* Is this multicast ? */ + if (*mgid == 0xff) { + mcast = __ipoib_mcast_find(dev, mgid); + + if (mcast && test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) { + list_del(&mcast->list); + rb_erase(&mcast->rb_node, &priv->multicast_tree); + list_add_tail(&mcast->list, &remove_list); + } + } + rcu_assign_pointer(*np, rcu_dereference_protected(neigh->hnext, lockdep_is_held(&priv->lock))); @@ -1191,6 +1207,8 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv) out_unlock: spin_unlock_irqrestore(&priv->lock, flags); + list_for_each_entry_safe(mcast, tmcast, &remove_list, list) + ipoib_mcast_leave(dev, mcast); } static void ipoib_reap_neigh(struct work_struct *work) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 09a1748f9d13..136cbefe00f8 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -153,7 +153,7 @@ static struct ipoib_mcast *ipoib_mcast_alloc(struct net_device *dev, return mcast; } -static struct ipoib_mcast *__ipoib_mcast_find(struct net_device *dev, void *mgid) +struct ipoib_mcast *__ipoib_mcast_find(struct net_device *dev, void *mgid) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct rb_node *n = priv->multicast_tree.rb_node; @@ -508,17 +508,19 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast) rec.hop_limit = priv->broadcast->mcmember.hop_limit; /* - * Historically Linux IPoIB has never properly supported SEND - * ONLY join. It emulated it by not providing all the required - * attributes, which is enough to prevent group creation and - * detect if there are full members or not. A major problem - * with supporting SEND ONLY is detecting when the group is - * auto-destroyed as IPoIB will cache the MLID.. + * Send-only IB Multicast joins do not work at the core + * IB layer yet, so we can't use them here. However, + * we are emulating an Ethernet multicast send, which + * does not require a multicast subscription and will + * still send properly. The most appropriate thing to + * do is to create the group if it doesn't exist as that + * most closely emulates the behavior, from a user space + * application perspecitive, of Ethernet multicast + * operation. For now, we do a full join, maybe later + * when the core IB layers support send only joins we + * will use them. */ -#if 1 - if (test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) - comp_mask &= ~IB_SA_MCMEMBER_REC_TRAFFIC_CLASS; -#else +#if 0 if (test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) rec.join_state = 4; #endif @@ -675,7 +677,7 @@ int ipoib_mcast_stop_thread(struct net_device *dev) return 0; } -static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast) +int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast) { struct ipoib_dev_priv *priv = netdev_priv(dev); int ret = 0; diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 1ace5d83a4d7..f58ff96b6cbb 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -97,6 +97,11 @@ unsigned int iser_max_sectors = ISER_DEF_MAX_SECTORS; module_param_named(max_sectors, iser_max_sectors, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(max_sectors, "Max number of sectors in a single scsi command (default:1024"); +bool iser_always_reg = true; +module_param_named(always_register, iser_always_reg, bool, S_IRUGO); +MODULE_PARM_DESC(always_register, + "Always register memory, even for continuous memory regions (default:true)"); + bool iser_pi_enable = false; module_param_named(pi_enable, iser_pi_enable, bool, S_IRUGO); MODULE_PARM_DESC(pi_enable, "Enable T10-PI offload support (default:disabled)"); diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h index 86f6583485ef..a5edd6ede692 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.h +++ b/drivers/infiniband/ulp/iser/iscsi_iser.h @@ -611,6 +611,7 @@ extern int iser_debug_level; extern bool iser_pi_enable; extern int iser_pi_guard; extern unsigned int iser_max_sectors; +extern bool iser_always_reg; int iser_assign_reg_ops(struct iser_device *device); diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c index 2493cc748db8..4c46d67d37a1 100644 --- a/drivers/infiniband/ulp/iser/iser_memory.c +++ b/drivers/infiniband/ulp/iser/iser_memory.c @@ -803,11 +803,12 @@ static int iser_reg_prot_sg(struct iscsi_iser_task *task, struct iser_data_buf *mem, struct iser_fr_desc *desc, + bool use_dma_key, struct iser_mem_reg *reg) { struct iser_device *device = task->iser_conn->ib_conn.device; - if (mem->dma_nents == 1) + if (use_dma_key) return iser_reg_dma(device, mem, reg); return device->reg_ops->reg_mem(task, mem, &desc->pi_ctx->rsc, reg); @@ -817,11 +818,12 @@ static int iser_reg_data_sg(struct iscsi_iser_task *task, struct iser_data_buf *mem, struct iser_fr_desc *desc, + bool use_dma_key, struct iser_mem_reg *reg) { struct iser_device *device = task->iser_conn->ib_conn.device; - if (mem->dma_nents == 1) + if (use_dma_key) return iser_reg_dma(device, mem, reg); return device->reg_ops->reg_mem(task, mem, &desc->rsc, reg); @@ -836,14 +838,17 @@ int iser_reg_rdma_mem(struct iscsi_iser_task *task, struct iser_mem_reg *reg = &task->rdma_reg[dir]; struct iser_mem_reg *data_reg; struct iser_fr_desc *desc = NULL; + bool use_dma_key; int err; err = iser_handle_unaligned_buf(task, mem, dir); if (unlikely(err)) return err; - if (mem->dma_nents != 1 || - scsi_get_prot_op(task->sc) != SCSI_PROT_NORMAL) { + use_dma_key = (mem->dma_nents == 1 && !iser_always_reg && + scsi_get_prot_op(task->sc) == SCSI_PROT_NORMAL); + + if (!use_dma_key) { desc = device->reg_ops->reg_desc_get(ib_conn); reg->mem_h = desc; } @@ -853,7 +858,7 @@ int iser_reg_rdma_mem(struct iscsi_iser_task *task, else data_reg = &task->desc.data_reg; - err = iser_reg_data_sg(task, mem, desc, data_reg); + err = iser_reg_data_sg(task, mem, desc, use_dma_key, data_reg); if (unlikely(err)) goto err_reg; @@ -866,7 +871,8 @@ int iser_reg_rdma_mem(struct iscsi_iser_task *task, if (unlikely(err)) goto err_reg; - err = iser_reg_prot_sg(task, mem, desc, prot_reg); + err = iser_reg_prot_sg(task, mem, desc, + use_dma_key, prot_reg); if (unlikely(err)) goto err_reg; } diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index ae70cc1463ac..85132d867bc8 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -133,11 +133,15 @@ static int iser_create_device_ib_res(struct iser_device *device) (unsigned long)comp); } - device->mr = ib_get_dma_mr(device->pd, IB_ACCESS_LOCAL_WRITE | - IB_ACCESS_REMOTE_WRITE | - IB_ACCESS_REMOTE_READ); - if (IS_ERR(device->mr)) - goto dma_mr_err; + if (!iser_always_reg) { + int access = IB_ACCESS_LOCAL_WRITE | + IB_ACCESS_REMOTE_WRITE | + IB_ACCESS_REMOTE_READ; + + device->mr = ib_get_dma_mr(device->pd, access); + if (IS_ERR(device->mr)) + goto dma_mr_err; + } INIT_IB_EVENT_HANDLER(&device->event_handler, device->ib_device, iser_event_handler); @@ -147,7 +151,8 @@ static int iser_create_device_ib_res(struct iser_device *device) return 0; handler_err: - ib_dereg_mr(device->mr); + if (device->mr) + ib_dereg_mr(device->mr); dma_mr_err: for (i = 0; i < device->comps_used; i++) tasklet_kill(&device->comps[i].tasklet); @@ -173,7 +178,6 @@ comps_err: static void iser_free_device_ib_res(struct iser_device *device) { int i; - BUG_ON(device->mr == NULL); for (i = 0; i < device->comps_used; i++) { struct iser_comp *comp = &device->comps[i]; @@ -184,7 +188,8 @@ static void iser_free_device_ib_res(struct iser_device *device) } (void)ib_unregister_event_handler(&device->event_handler); - (void)ib_dereg_mr(device->mr); + if (device->mr) + (void)ib_dereg_mr(device->mr); ib_dealloc_pd(device->pd); kfree(device->comps); diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index dc439a40da3f..aa59037d7504 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -238,8 +238,6 @@ isert_alloc_rx_descriptors(struct isert_conn *isert_conn) rx_sg->lkey = device->pd->local_dma_lkey; } - isert_conn->rx_desc_head = 0; - return 0; dma_map_fail: @@ -634,7 +632,7 @@ static void isert_init_conn(struct isert_conn *isert_conn) { isert_conn->state = ISER_CONN_INIT; - INIT_LIST_HEAD(&isert_conn->accept_node); + INIT_LIST_HEAD(&isert_conn->node); init_completion(&isert_conn->login_comp); init_completion(&isert_conn->login_req_comp); init_completion(&isert_conn->wait); @@ -762,28 +760,15 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) ret = isert_rdma_post_recvl(isert_conn); if (ret) goto out_conn_dev; - /* - * Obtain the second reference now before isert_rdma_accept() to - * ensure that any initiator generated REJECT CM event that occurs - * asynchronously won't drop the last reference until the error path - * in iscsi_target_login_sess_out() does it's ->iscsit_free_conn() -> - * isert_free_conn() -> isert_put_conn() -> kref_put(). - */ - if (!kref_get_unless_zero(&isert_conn->kref)) { - isert_warn("conn %p connect_release is running\n", isert_conn); - goto out_conn_dev; - } ret = isert_rdma_accept(isert_conn); if (ret) goto out_conn_dev; - mutex_lock(&isert_np->np_accept_mutex); - list_add_tail(&isert_conn->accept_node, &isert_np->np_accept_list); - mutex_unlock(&isert_np->np_accept_mutex); + mutex_lock(&isert_np->mutex); + list_add_tail(&isert_conn->node, &isert_np->accepted); + mutex_unlock(&isert_np->mutex); - isert_info("np %p: Allow accept_np to continue\n", np); - up(&isert_np->np_sem); return 0; out_conn_dev: @@ -831,13 +816,21 @@ static void isert_connected_handler(struct rdma_cm_id *cma_id) { struct isert_conn *isert_conn = cma_id->qp->qp_context; + struct isert_np *isert_np = cma_id->context; isert_info("conn %p\n", isert_conn); mutex_lock(&isert_conn->mutex); - if (isert_conn->state != ISER_CONN_FULL_FEATURE) - isert_conn->state = ISER_CONN_UP; + isert_conn->state = ISER_CONN_UP; + kref_get(&isert_conn->kref); mutex_unlock(&isert_conn->mutex); + + mutex_lock(&isert_np->mutex); + list_move_tail(&isert_conn->node, &isert_np->pending); + mutex_unlock(&isert_np->mutex); + + isert_info("np %p: Allow accept_np to continue\n", isert_np); + up(&isert_np->sem); } static void @@ -903,14 +896,14 @@ isert_np_cma_handler(struct isert_np *isert_np, switch (event) { case RDMA_CM_EVENT_DEVICE_REMOVAL: - isert_np->np_cm_id = NULL; + isert_np->cm_id = NULL; break; case RDMA_CM_EVENT_ADDR_CHANGE: - isert_np->np_cm_id = isert_setup_id(isert_np); - if (IS_ERR(isert_np->np_cm_id)) { + isert_np->cm_id = isert_setup_id(isert_np); + if (IS_ERR(isert_np->cm_id)) { isert_err("isert np %p setup id failed: %ld\n", - isert_np, PTR_ERR(isert_np->np_cm_id)); - isert_np->np_cm_id = NULL; + isert_np, PTR_ERR(isert_np->cm_id)); + isert_np->cm_id = NULL; } break; default: @@ -929,7 +922,7 @@ isert_disconnected_handler(struct rdma_cm_id *cma_id, struct isert_conn *isert_conn; bool terminating = false; - if (isert_np->np_cm_id == cma_id) + if (isert_np->cm_id == cma_id) return isert_np_cma_handler(cma_id->context, event); isert_conn = cma_id->qp->qp_context; @@ -945,13 +938,13 @@ isert_disconnected_handler(struct rdma_cm_id *cma_id, if (terminating) goto out; - mutex_lock(&isert_np->np_accept_mutex); - if (!list_empty(&isert_conn->accept_node)) { - list_del_init(&isert_conn->accept_node); + mutex_lock(&isert_np->mutex); + if (!list_empty(&isert_conn->node)) { + list_del_init(&isert_conn->node); isert_put_conn(isert_conn); queue_work(isert_release_wq, &isert_conn->release_work); } - mutex_unlock(&isert_np->np_accept_mutex); + mutex_unlock(&isert_np->mutex); out: return 0; @@ -962,6 +955,7 @@ isert_connect_error(struct rdma_cm_id *cma_id) { struct isert_conn *isert_conn = cma_id->qp->qp_context; + list_del_init(&isert_conn->node); isert_conn->cm_id = NULL; isert_put_conn(isert_conn); @@ -1006,35 +1000,51 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) } static int -isert_post_recv(struct isert_conn *isert_conn, u32 count) +isert_post_recvm(struct isert_conn *isert_conn, u32 count) { struct ib_recv_wr *rx_wr, *rx_wr_failed; int i, ret; - unsigned int rx_head = isert_conn->rx_desc_head; struct iser_rx_desc *rx_desc; for (rx_wr = isert_conn->rx_wr, i = 0; i < count; i++, rx_wr++) { - rx_desc = &isert_conn->rx_descs[rx_head]; - rx_wr->wr_id = (uintptr_t)rx_desc; - rx_wr->sg_list = &rx_desc->rx_sg; - rx_wr->num_sge = 1; - rx_wr->next = rx_wr + 1; - rx_head = (rx_head + 1) & (ISERT_QP_MAX_RECV_DTOS - 1); + rx_desc = &isert_conn->rx_descs[i]; + rx_wr->wr_id = (uintptr_t)rx_desc; + rx_wr->sg_list = &rx_desc->rx_sg; + rx_wr->num_sge = 1; + rx_wr->next = rx_wr + 1; } - rx_wr--; rx_wr->next = NULL; /* mark end of work requests list */ isert_conn->post_recv_buf_count += count; ret = ib_post_recv(isert_conn->qp, isert_conn->rx_wr, - &rx_wr_failed); + &rx_wr_failed); if (ret) { isert_err("ib_post_recv() failed with ret: %d\n", ret); isert_conn->post_recv_buf_count -= count; - } else { - isert_dbg("Posted %d RX buffers\n", count); - isert_conn->rx_desc_head = rx_head; } + + return ret; +} + +static int +isert_post_recv(struct isert_conn *isert_conn, struct iser_rx_desc *rx_desc) +{ + struct ib_recv_wr *rx_wr_failed, rx_wr; + int ret; + + rx_wr.wr_id = (uintptr_t)rx_desc; + rx_wr.sg_list = &rx_desc->rx_sg; + rx_wr.num_sge = 1; + rx_wr.next = NULL; + + isert_conn->post_recv_buf_count++; + ret = ib_post_recv(isert_conn->qp, &rx_wr, &rx_wr_failed); + if (ret) { + isert_err("ib_post_recv() failed with ret: %d\n", ret); + isert_conn->post_recv_buf_count--; + } + return ret; } @@ -1205,7 +1215,8 @@ isert_put_login_tx(struct iscsi_conn *conn, struct iscsi_login *login, if (ret) return ret; - ret = isert_post_recv(isert_conn, ISERT_MIN_POSTED_RX); + ret = isert_post_recvm(isert_conn, + ISERT_QP_MAX_RECV_DTOS); if (ret) return ret; @@ -1278,7 +1289,7 @@ isert_rx_login_req(struct isert_conn *isert_conn) } static struct iscsi_cmd -*isert_allocate_cmd(struct iscsi_conn *conn) +*isert_allocate_cmd(struct iscsi_conn *conn, struct iser_rx_desc *rx_desc) { struct isert_conn *isert_conn = conn->context; struct isert_cmd *isert_cmd; @@ -1292,6 +1303,7 @@ static struct iscsi_cmd isert_cmd = iscsit_priv_cmd(cmd); isert_cmd->conn = isert_conn; isert_cmd->iscsi_cmd = cmd; + isert_cmd->rx_desc = rx_desc; return cmd; } @@ -1303,9 +1315,9 @@ isert_handle_scsi_cmd(struct isert_conn *isert_conn, { struct iscsi_conn *conn = isert_conn->conn; struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)buf; - struct scatterlist *sg; int imm_data, imm_data_len, unsol_data, sg_nents, rc; bool dump_payload = false; + unsigned int data_len; rc = iscsit_setup_scsi_cmd(conn, cmd, buf); if (rc < 0) @@ -1314,7 +1326,10 @@ isert_handle_scsi_cmd(struct isert_conn *isert_conn, imm_data = cmd->immediate_data; imm_data_len = cmd->first_burst_len; unsol_data = cmd->unsolicited_data; + data_len = cmd->se_cmd.data_length; + if (imm_data && imm_data_len == data_len) + cmd->se_cmd.se_cmd_flags |= SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC; rc = iscsit_process_scsi_cmd(conn, cmd, hdr); if (rc < 0) { return 0; @@ -1326,13 +1341,20 @@ isert_handle_scsi_cmd(struct isert_conn *isert_conn, if (!imm_data) return 0; - sg = &cmd->se_cmd.t_data_sg[0]; - sg_nents = max(1UL, DIV_ROUND_UP(imm_data_len, PAGE_SIZE)); - - isert_dbg("Copying Immediate SG: %p sg_nents: %u from %p imm_data_len: %d\n", - sg, sg_nents, &rx_desc->data[0], imm_data_len); - - sg_copy_from_buffer(sg, sg_nents, &rx_desc->data[0], imm_data_len); + if (imm_data_len != data_len) { + sg_nents = max(1UL, DIV_ROUND_UP(imm_data_len, PAGE_SIZE)); + sg_copy_from_buffer(cmd->se_cmd.t_data_sg, sg_nents, + &rx_desc->data[0], imm_data_len); + isert_dbg("Copy Immediate sg_nents: %u imm_data_len: %d\n", + sg_nents, imm_data_len); + } else { + sg_init_table(&isert_cmd->sg, 1); + cmd->se_cmd.t_data_sg = &isert_cmd->sg; + cmd->se_cmd.t_data_nents = 1; + sg_set_buf(&isert_cmd->sg, &rx_desc->data[0], imm_data_len); + isert_dbg("Transfer Immediate imm_data_len: %d\n", + imm_data_len); + } cmd->write_data_done += imm_data_len; @@ -1407,6 +1429,15 @@ isert_handle_iscsi_dataout(struct isert_conn *isert_conn, if (rc < 0) return rc; + /* + * multiple data-outs on the same command can arrive - + * so post the buffer before hand + */ + rc = isert_post_recv(isert_conn, rx_desc); + if (rc) { + isert_err("ib_post_recv failed with %d\n", rc); + return rc; + } return 0; } @@ -1479,7 +1510,7 @@ isert_rx_opcode(struct isert_conn *isert_conn, struct iser_rx_desc *rx_desc, switch (opcode) { case ISCSI_OP_SCSI_CMD: - cmd = isert_allocate_cmd(conn); + cmd = isert_allocate_cmd(conn, rx_desc); if (!cmd) break; @@ -1493,7 +1524,7 @@ isert_rx_opcode(struct isert_conn *isert_conn, struct iser_rx_desc *rx_desc, rx_desc, (unsigned char *)hdr); break; case ISCSI_OP_NOOP_OUT: - cmd = isert_allocate_cmd(conn); + cmd = isert_allocate_cmd(conn, rx_desc); if (!cmd) break; @@ -1506,7 +1537,7 @@ isert_rx_opcode(struct isert_conn *isert_conn, struct iser_rx_desc *rx_desc, (unsigned char *)hdr); break; case ISCSI_OP_SCSI_TMFUNC: - cmd = isert_allocate_cmd(conn); + cmd = isert_allocate_cmd(conn, rx_desc); if (!cmd) break; @@ -1514,22 +1545,20 @@ isert_rx_opcode(struct isert_conn *isert_conn, struct iser_rx_desc *rx_desc, (unsigned char *)hdr); break; case ISCSI_OP_LOGOUT: - cmd = isert_allocate_cmd(conn); + cmd = isert_allocate_cmd(conn, rx_desc); if (!cmd) break; ret = iscsit_handle_logout_cmd(conn, cmd, (unsigned char *)hdr); break; case ISCSI_OP_TEXT: - if (be32_to_cpu(hdr->ttt) != 0xFFFFFFFF) { + if (be32_to_cpu(hdr->ttt) != 0xFFFFFFFF) cmd = iscsit_find_cmd_from_itt(conn, hdr->itt); - if (!cmd) - break; - } else { - cmd = isert_allocate_cmd(conn); - if (!cmd) - break; - } + else + cmd = isert_allocate_cmd(conn, rx_desc); + + if (!cmd) + break; isert_cmd = iscsit_priv_cmd(cmd); ret = isert_handle_text_cmd(isert_conn, isert_cmd, cmd, @@ -1589,7 +1618,7 @@ isert_rcv_completion(struct iser_rx_desc *desc, struct ib_device *ib_dev = isert_conn->cm_id->device; struct iscsi_hdr *hdr; u64 rx_dma; - int rx_buflen, outstanding; + int rx_buflen; if ((char *)desc == isert_conn->login_req_buf) { rx_dma = isert_conn->login_req_dma; @@ -1629,22 +1658,6 @@ isert_rcv_completion(struct iser_rx_desc *desc, DMA_FROM_DEVICE); isert_conn->post_recv_buf_count--; - isert_dbg("Decremented post_recv_buf_count: %d\n", - isert_conn->post_recv_buf_count); - - if ((char *)desc == isert_conn->login_req_buf) - return; - - outstanding = isert_conn->post_recv_buf_count; - if (outstanding + ISERT_MIN_POSTED_RX <= ISERT_QP_MAX_RECV_DTOS) { - int err, count = min(ISERT_QP_MAX_RECV_DTOS - outstanding, - ISERT_MIN_POSTED_RX); - err = isert_post_recv(isert_conn, count); - if (err) { - isert_err("isert_post_recv() count: %d failed, %d\n", - count, err); - } - } } static int @@ -2156,6 +2169,12 @@ isert_post_response(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd) struct ib_send_wr *wr_failed; int ret; + ret = isert_post_recv(isert_conn, isert_cmd->rx_desc); + if (ret) { + isert_err("ib_post_recv failed with %d\n", ret); + return ret; + } + ret = ib_post_send(isert_conn->qp, &isert_cmd->tx_desc.send_wr, &wr_failed); if (ret) { @@ -2950,6 +2969,12 @@ isert_put_datain(struct iscsi_conn *conn, struct iscsi_cmd *cmd) &isert_cmd->tx_desc.send_wr); isert_cmd->rdma_wr.s_send_wr.next = &isert_cmd->tx_desc.send_wr; wr->send_wr_num += 1; + + rc = isert_post_recv(isert_conn, isert_cmd->rx_desc); + if (rc) { + isert_err("ib_post_recv failed with %d\n", rc); + return rc; + } } rc = ib_post_send(isert_conn->qp, wr->send_wr, &wr_failed); @@ -2999,9 +3024,16 @@ isert_get_dataout(struct iscsi_conn *conn, struct iscsi_cmd *cmd, bool recovery) static int isert_immediate_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state) { - int ret; + struct isert_cmd *isert_cmd = iscsit_priv_cmd(cmd); + int ret = 0; switch (state) { + case ISTATE_REMOVE: + spin_lock_bh(&conn->cmd_lock); + list_del_init(&cmd->i_conn_node); + spin_unlock_bh(&conn->cmd_lock); + isert_put_cmd(isert_cmd, true); + break; case ISTATE_SEND_NOPIN_WANT_RESPONSE: ret = isert_put_nopin(cmd, conn, false); break; @@ -3095,7 +3127,7 @@ out: static int isert_setup_np(struct iscsi_np *np, - struct __kernel_sockaddr_storage *ksockaddr) + struct sockaddr_storage *ksockaddr) { struct isert_np *isert_np; struct rdma_cm_id *isert_lid; @@ -3106,10 +3138,10 @@ isert_setup_np(struct iscsi_np *np, isert_err("Unable to allocate struct isert_np\n"); return -ENOMEM; } - sema_init(&isert_np->np_sem, 0); - mutex_init(&isert_np->np_accept_mutex); - INIT_LIST_HEAD(&isert_np->np_accept_list); - init_completion(&isert_np->np_login_comp); + sema_init(&isert_np->sem, 0); + mutex_init(&isert_np->mutex); + INIT_LIST_HEAD(&isert_np->accepted); + INIT_LIST_HEAD(&isert_np->pending); isert_np->np = np; /* @@ -3117,7 +3149,7 @@ isert_setup_np(struct iscsi_np *np, * in iscsi_target_configfs.c code.. */ memcpy(&np->np_sockaddr, ksockaddr, - sizeof(struct __kernel_sockaddr_storage)); + sizeof(struct sockaddr_storage)); isert_lid = isert_setup_id(isert_np); if (IS_ERR(isert_lid)) { @@ -3125,7 +3157,7 @@ isert_setup_np(struct iscsi_np *np, goto out; } - isert_np->np_cm_id = isert_lid; + isert_np->cm_id = isert_lid; np->np_context = isert_np; return 0; @@ -3199,32 +3231,11 @@ isert_set_conn_info(struct iscsi_np *np, struct iscsi_conn *conn, { struct rdma_cm_id *cm_id = isert_conn->cm_id; struct rdma_route *cm_route = &cm_id->route; - struct sockaddr_in *sock_in; - struct sockaddr_in6 *sock_in6; conn->login_family = np->np_sockaddr.ss_family; - if (np->np_sockaddr.ss_family == AF_INET6) { - sock_in6 = (struct sockaddr_in6 *)&cm_route->addr.dst_addr; - snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI6c", - &sock_in6->sin6_addr.in6_u); - conn->login_port = ntohs(sock_in6->sin6_port); - - sock_in6 = (struct sockaddr_in6 *)&cm_route->addr.src_addr; - snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI6c", - &sock_in6->sin6_addr.in6_u); - conn->local_port = ntohs(sock_in6->sin6_port); - } else { - sock_in = (struct sockaddr_in *)&cm_route->addr.dst_addr; - sprintf(conn->login_ip, "%pI4", - &sock_in->sin_addr.s_addr); - conn->login_port = ntohs(sock_in->sin_port); - - sock_in = (struct sockaddr_in *)&cm_route->addr.src_addr; - sprintf(conn->local_ip, "%pI4", - &sock_in->sin_addr.s_addr); - conn->local_port = ntohs(sock_in->sin_port); - } + conn->login_sockaddr = cm_route->addr.dst_addr; + conn->local_sockaddr = cm_route->addr.src_addr; } static int @@ -3235,7 +3246,7 @@ isert_accept_np(struct iscsi_np *np, struct iscsi_conn *conn) int ret; accept_wait: - ret = down_interruptible(&isert_np->np_sem); + ret = down_interruptible(&isert_np->sem); if (ret) return -ENODEV; @@ -3252,15 +3263,15 @@ accept_wait: } spin_unlock_bh(&np->np_thread_lock); - mutex_lock(&isert_np->np_accept_mutex); - if (list_empty(&isert_np->np_accept_list)) { - mutex_unlock(&isert_np->np_accept_mutex); + mutex_lock(&isert_np->mutex); + if (list_empty(&isert_np->pending)) { + mutex_unlock(&isert_np->mutex); goto accept_wait; } - isert_conn = list_first_entry(&isert_np->np_accept_list, - struct isert_conn, accept_node); - list_del_init(&isert_conn->accept_node); - mutex_unlock(&isert_np->np_accept_mutex); + isert_conn = list_first_entry(&isert_np->pending, + struct isert_conn, node); + list_del_init(&isert_conn->node); + mutex_unlock(&isert_np->mutex); conn->context = isert_conn; isert_conn->conn = conn; @@ -3278,28 +3289,39 @@ isert_free_np(struct iscsi_np *np) struct isert_np *isert_np = np->np_context; struct isert_conn *isert_conn, *n; - if (isert_np->np_cm_id) - rdma_destroy_id(isert_np->np_cm_id); + if (isert_np->cm_id) + rdma_destroy_id(isert_np->cm_id); /* * FIXME: At this point we don't have a good way to insure * that at this point we don't have hanging connections that * completed RDMA establishment but didn't start iscsi login * process. So work-around this by cleaning up what ever piled - * up in np_accept_list. + * up in accepted and pending lists. */ - mutex_lock(&isert_np->np_accept_mutex); - if (!list_empty(&isert_np->np_accept_list)) { - isert_info("Still have isert connections, cleaning up...\n"); + mutex_lock(&isert_np->mutex); + if (!list_empty(&isert_np->pending)) { + isert_info("Still have isert pending connections\n"); + list_for_each_entry_safe(isert_conn, n, + &isert_np->pending, + node) { + isert_info("cleaning isert_conn %p state (%d)\n", + isert_conn, isert_conn->state); + isert_connect_release(isert_conn); + } + } + + if (!list_empty(&isert_np->accepted)) { + isert_info("Still have isert accepted connections\n"); list_for_each_entry_safe(isert_conn, n, - &isert_np->np_accept_list, - accept_node) { + &isert_np->accepted, + node) { isert_info("cleaning isert_conn %p state (%d)\n", isert_conn, isert_conn->state); isert_connect_release(isert_conn); } } - mutex_unlock(&isert_np->np_accept_mutex); + mutex_unlock(&isert_np->mutex); np->np_context = NULL; kfree(isert_np); @@ -3366,6 +3388,41 @@ isert_wait4flush(struct isert_conn *isert_conn) wait_for_completion(&isert_conn->wait_comp_err); } +/** + * isert_put_unsol_pending_cmds() - Drop commands waiting for + * unsolicitate dataout + * @conn: iscsi connection + * + * We might still have commands that are waiting for unsolicited + * dataouts messages. We must put the extra reference on those + * before blocking on the target_wait_for_session_cmds + */ +static void +isert_put_unsol_pending_cmds(struct iscsi_conn *conn) +{ + struct iscsi_cmd *cmd, *tmp; + static LIST_HEAD(drop_cmd_list); + + spin_lock_bh(&conn->cmd_lock); + list_for_each_entry_safe(cmd, tmp, &conn->conn_cmd_list, i_conn_node) { + if ((cmd->cmd_flags & ICF_NON_IMMEDIATE_UNSOLICITED_DATA) && + (cmd->write_data_done < conn->sess->sess_ops->FirstBurstLength) && + (cmd->write_data_done < cmd->se_cmd.data_length)) + list_move_tail(&cmd->i_conn_node, &drop_cmd_list); + } + spin_unlock_bh(&conn->cmd_lock); + + list_for_each_entry_safe(cmd, tmp, &drop_cmd_list, i_conn_node) { + list_del_init(&cmd->i_conn_node); + if (cmd->i_state != ISTATE_REMOVE) { + struct isert_cmd *isert_cmd = iscsit_priv_cmd(cmd); + + isert_info("conn %p dropping cmd %p\n", conn, cmd); + isert_put_cmd(isert_cmd, true); + } + } +} + static void isert_wait_conn(struct iscsi_conn *conn) { struct isert_conn *isert_conn = conn->context; @@ -3384,8 +3441,9 @@ static void isert_wait_conn(struct iscsi_conn *conn) isert_conn_terminate(isert_conn); mutex_unlock(&isert_conn->mutex); - isert_wait4cmds(conn); isert_wait4flush(isert_conn); + isert_put_unsol_pending_cmds(conn); + isert_wait4cmds(conn); isert_wait4logout(isert_conn); queue_work(isert_release_wq, &isert_conn->release_work); diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h index 6a04ba3c0f72..c5b99bcecbcf 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.h +++ b/drivers/infiniband/ulp/isert/ib_isert.h @@ -113,7 +113,6 @@ enum { }; struct isert_rdma_wr { - struct list_head wr_list; struct isert_cmd *isert_cmd; enum iser_ib_op_code iser_ib_op; struct ib_sge *ib_sge; @@ -134,14 +133,13 @@ struct isert_cmd { uint64_t write_va; u64 pdu_buf_dma; u32 pdu_buf_len; - u32 read_va_off; - u32 write_va_off; - u32 rdma_wr_num; struct isert_conn *conn; struct iscsi_cmd *iscsi_cmd; struct iser_tx_desc tx_desc; + struct iser_rx_desc *rx_desc; struct isert_rdma_wr rdma_wr; struct work_struct comp_work; + struct scatterlist sg; }; struct isert_device; @@ -159,11 +157,10 @@ struct isert_conn { u64 login_req_dma; int login_req_len; u64 login_rsp_dma; - unsigned int rx_desc_head; struct iser_rx_desc *rx_descs; - struct ib_recv_wr rx_wr[ISERT_MIN_POSTED_RX]; + struct ib_recv_wr rx_wr[ISERT_QP_MAX_RECV_DTOS]; struct iscsi_conn *conn; - struct list_head accept_node; + struct list_head node; struct completion login_comp; struct completion login_req_comp; struct iser_tx_desc login_tx_desc; @@ -222,9 +219,9 @@ struct isert_device { struct isert_np { struct iscsi_np *np; - struct semaphore np_sem; - struct rdma_cm_id *np_cm_id; - struct mutex np_accept_mutex; - struct list_head np_accept_list; - struct completion np_login_comp; + struct semaphore sem; + struct rdma_cm_id *cm_id; + struct mutex mutex; + struct list_head accepted; + struct list_head pending; }; diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 9d35499faca4..08d496411f75 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -290,19 +290,14 @@ static int evdev_flush(struct file *file, fl_owner_t id) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; - int retval; - retval = mutex_lock_interruptible(&evdev->mutex); - if (retval) - return retval; + mutex_lock(&evdev->mutex); - if (!evdev->exist || client->revoked) - retval = -ENODEV; - else - retval = input_flush_device(&evdev->handle, file); + if (evdev->exist && !client->revoked) + input_flush_device(&evdev->handle, file); mutex_unlock(&evdev->mutex); - return retval; + return 0; } static void evdev_free(struct device *dev) diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index 56eb471b5576..4215b5382092 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -196,6 +196,7 @@ config JOYSTICK_TWIDJOY config JOYSTICK_ZHENHUA tristate "5-byte Zhenhua RC transmitter" select SERIO + select BITREVERSE help Say Y here if you have a Zhen Hua PPM-4CH transmitter which is supplied with a ready to fly micro electric indoor helicopters diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index d2ea863d6a45..2165f3dd328b 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -5,8 +5,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * <<Power management needs to be implemented>>. */ #include <linux/clk.h> diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 1f7e15ca5fbe..4f5ef5bb535b 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -118,6 +118,7 @@ static const struct of_device_id ab8500_ponkey_match[] = { { .compatible = "stericsson,ab8500-ponkey", }, {} }; +MODULE_DEVICE_TABLE(of, ab8500_ponkey_match); #endif static struct platform_driver ab8500_ponkey_driver = { diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index e82edf810d1f..f2261ab54701 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -173,6 +173,7 @@ static const struct of_device_id pwm_beeper_match[] = { { .compatible = "pwm-beeper", }, { }, }; +MODULE_DEVICE_TABLE(of, pwm_beeper_match); #endif static struct platform_driver pwm_beeper_driver = { diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c index 6bf3f1082f71..a804705eb04a 100644 --- a/drivers/input/misc/regulator-haptic.c +++ b/drivers/input/misc/regulator-haptic.c @@ -249,6 +249,7 @@ static const struct of_device_id regulator_haptic_dt_match[] = { { .compatible = "regulator-haptic" }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, regulator_haptic_dt_match); static struct platform_driver regulator_haptic_driver = { .probe = regulator_haptic_probe, diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index 54116e544c96..6f997aa49183 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -253,6 +253,7 @@ static const struct of_device_id bbc_beep_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, bbc_beep_match); static struct platform_driver bbc_beep_driver = { .driver = { @@ -332,6 +333,7 @@ static const struct of_device_id grover_beep_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, grover_beep_match); static struct platform_driver grover_beep_driver = { .driver = { diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index e2b7420eed97..fa945304b9a5 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1170,6 +1170,7 @@ static const struct acpi_device_id elan_acpi_id[] = { { "ELAN0000", 0 }, { "ELAN0100", 0 }, { "ELAN0600", 0 }, + { "ELAN1000", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, elan_acpi_id); diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index c9c98f0ab284..db91de539ee3 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -877,7 +877,7 @@ static int __init i8042_check_aux(void) static int i8042_controller_check(void) { if (i8042_flush()) { - pr_err("No controller found\n"); + pr_info("No controller found\n"); return -ENODEV; } diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 059edeb7f04a..600dcceff542 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -479,6 +479,18 @@ config TOUCHSCREEN_MTOUCH To compile this driver as a module, choose M here: the module will be called mtouch. +config TOUCHSCREEN_IMX6UL_TSC + tristate "Freescale i.MX6UL touchscreen controller" + depends on (OF && GPIOLIB) || COMPILE_TEST + help + Say Y here if you have a Freescale i.MX6UL, and want to + use the internal touchscreen controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called imx6ul_tsc. + config TOUCHSCREEN_INEXIO tristate "iNexio serial touchscreens" select SERIO @@ -1040,4 +1052,16 @@ config TOUCHSCREEN_ZFORCE To compile this driver as a module, choose M here: the module will be called zforce_ts. +config TOUCHSCREEN_COLIBRI_VF50 + tristate "Toradex Colibri on board touchscreen driver" + depends on GPIOLIB && IIO && VF610_ADC + help + Say Y here if you have a Colibri VF50 and plan to use + the on-board provided 4-wire touchscreen driver. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called colibri_vf50_ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index c85aae23e7f8..1b79cc09744a 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o +obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o @@ -85,3 +86,4 @@ obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o +obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c new file mode 100644 index 000000000000..5d4903a402cc --- /dev/null +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -0,0 +1,386 @@ +/* + * Toradex Colibri VF50 Touchscreen driver + * + * Copyright 2015 Toradex AG + * + * Originally authored by Stefan Agner for 3.0 kernel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/iio/consumer.h> +#include <linux/iio/types.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> + +#define DRIVER_NAME "colibri-vf50-ts" +#define DRV_VERSION "1.0" + +#define VF_ADC_MAX ((1 << 12) - 1) + +#define COLI_TOUCH_MIN_DELAY_US 1000 +#define COLI_TOUCH_MAX_DELAY_US 2000 +#define COLI_PULLUP_MIN_DELAY_US 10000 +#define COLI_PULLUP_MAX_DELAY_US 11000 +#define COLI_TOUCH_NO_OF_AVGS 5 +#define COLI_TOUCH_REQ_ADC_CHAN 4 + +struct vf50_touch_device { + struct platform_device *pdev; + struct input_dev *ts_input; + struct iio_channel *channels; + struct gpio_desc *gpio_xp; + struct gpio_desc *gpio_xm; + struct gpio_desc *gpio_yp; + struct gpio_desc *gpio_ym; + int pen_irq; + int min_pressure; + bool stop_touchscreen; +}; + +/* + * Enables given plates and measures touch parameters using ADC + */ +static int adc_ts_measure(struct iio_channel *channel, + struct gpio_desc *plate_p, struct gpio_desc *plate_m) +{ + int i, value = 0, val = 0; + int error; + + gpiod_set_value(plate_p, 1); + gpiod_set_value(plate_m, 1); + + usleep_range(COLI_TOUCH_MIN_DELAY_US, COLI_TOUCH_MAX_DELAY_US); + + for (i = 0; i < COLI_TOUCH_NO_OF_AVGS; i++) { + error = iio_read_channel_raw(channel, &val); + if (error < 0) { + value = error; + goto error_iio_read; + } + + value += val; + } + + value /= COLI_TOUCH_NO_OF_AVGS; + +error_iio_read: + gpiod_set_value(plate_p, 0); + gpiod_set_value(plate_m, 0); + + return value; +} + +/* + * Enable touch detection using falling edge detection on XM + */ +static void vf50_ts_enable_touch_detection(struct vf50_touch_device *vf50_ts) +{ + /* Enable plate YM (needs to be strong GND, high active) */ + gpiod_set_value(vf50_ts->gpio_ym, 1); + + /* + * Let the platform mux to idle state in order to enable + * Pull-Up on GPIO + */ + pinctrl_pm_select_idle_state(&vf50_ts->pdev->dev); + + /* Wait for the pull-up to be stable on high */ + usleep_range(COLI_PULLUP_MIN_DELAY_US, COLI_PULLUP_MAX_DELAY_US); +} + +/* + * ADC touch screen sampling bottom half irq handler + */ +static irqreturn_t vf50_ts_irq_bh(int irq, void *private) +{ + struct vf50_touch_device *vf50_ts = private; + struct device *dev = &vf50_ts->pdev->dev; + int val_x, val_y, val_z1, val_z2, val_p = 0; + bool discard_val_on_start = true; + + /* Disable the touch detection plates */ + gpiod_set_value(vf50_ts->gpio_ym, 0); + + /* Let the platform mux to default state in order to mux as ADC */ + pinctrl_pm_select_default_state(dev); + + while (!vf50_ts->stop_touchscreen) { + /* X-Direction */ + val_x = adc_ts_measure(&vf50_ts->channels[0], + vf50_ts->gpio_xp, vf50_ts->gpio_xm); + if (val_x < 0) + break; + + /* Y-Direction */ + val_y = adc_ts_measure(&vf50_ts->channels[1], + vf50_ts->gpio_yp, vf50_ts->gpio_ym); + if (val_y < 0) + break; + + /* + * Touch pressure + * Measure on XP/YM + */ + val_z1 = adc_ts_measure(&vf50_ts->channels[2], + vf50_ts->gpio_yp, vf50_ts->gpio_xm); + if (val_z1 < 0) + break; + val_z2 = adc_ts_measure(&vf50_ts->channels[3], + vf50_ts->gpio_yp, vf50_ts->gpio_xm); + if (val_z2 < 0) + break; + + /* Validate signal (avoid calculation using noise) */ + if (val_z1 > 64 && val_x > 64) { + /* + * Calculate resistance between the plates + * lower resistance means higher pressure + */ + int r_x = (1000 * val_x) / VF_ADC_MAX; + + val_p = (r_x * val_z2) / val_z1 - r_x; + + } else { + val_p = 2000; + } + + val_p = 2000 - val_p; + dev_dbg(dev, + "Measured values: x: %d, y: %d, z1: %d, z2: %d, p: %d\n", + val_x, val_y, val_z1, val_z2, val_p); + + /* + * If touch pressure is too low, stop measuring and reenable + * touch detection + */ + if (val_p < vf50_ts->min_pressure || val_p > 2000) + break; + + /* + * The pressure may not be enough for the first x and the + * second y measurement, but, the pressure is ok when the + * driver is doing the third and fourth measurement. To + * take care of this, we drop the first measurement always. + */ + if (discard_val_on_start) { + discard_val_on_start = false; + } else { + /* + * Report touch position and sleep for + * the next measurement. + */ + input_report_abs(vf50_ts->ts_input, + ABS_X, VF_ADC_MAX - val_x); + input_report_abs(vf50_ts->ts_input, + ABS_Y, VF_ADC_MAX - val_y); + input_report_abs(vf50_ts->ts_input, + ABS_PRESSURE, val_p); + input_report_key(vf50_ts->ts_input, BTN_TOUCH, 1); + input_sync(vf50_ts->ts_input); + } + + usleep_range(COLI_PULLUP_MIN_DELAY_US, + COLI_PULLUP_MAX_DELAY_US); + } + + /* Report no more touch, re-enable touch detection */ + input_report_abs(vf50_ts->ts_input, ABS_PRESSURE, 0); + input_report_key(vf50_ts->ts_input, BTN_TOUCH, 0); + input_sync(vf50_ts->ts_input); + + vf50_ts_enable_touch_detection(vf50_ts); + + return IRQ_HANDLED; +} + +static int vf50_ts_open(struct input_dev *dev_input) +{ + struct vf50_touch_device *touchdev = input_get_drvdata(dev_input); + struct device *dev = &touchdev->pdev->dev; + + dev_dbg(dev, "Input device %s opened, starting touch detection\n", + dev_input->name); + + touchdev->stop_touchscreen = false; + + /* Mux detection before request IRQ, wait for pull-up to settle */ + vf50_ts_enable_touch_detection(touchdev); + + return 0; +} + +static void vf50_ts_close(struct input_dev *dev_input) +{ + struct vf50_touch_device *touchdev = input_get_drvdata(dev_input); + struct device *dev = &touchdev->pdev->dev; + + touchdev->stop_touchscreen = true; + + /* Make sure IRQ is not running past close */ + mb(); + synchronize_irq(touchdev->pen_irq); + + gpiod_set_value(touchdev->gpio_ym, 0); + pinctrl_pm_select_default_state(dev); + + dev_dbg(dev, "Input device %s closed, disable touch detection\n", + dev_input->name); +} + +static int vf50_ts_get_gpiod(struct device *dev, struct gpio_desc **gpio_d, + const char *con_id, enum gpiod_flags flags) +{ + int error; + + *gpio_d = devm_gpiod_get(dev, con_id, flags); + if (IS_ERR(*gpio_d)) { + error = PTR_ERR(*gpio_d); + dev_err(dev, "Could not get gpio_%s %d\n", con_id, error); + return error; + } + + return 0; +} + +static void vf50_ts_channel_release(void *data) +{ + struct iio_channel *channels = data; + + iio_channel_release_all(channels); +} + +static int vf50_ts_probe(struct platform_device *pdev) +{ + struct input_dev *input; + struct iio_channel *channels; + struct device *dev = &pdev->dev; + struct vf50_touch_device *touchdev; + int num_adc_channels; + int error; + + channels = iio_channel_get_all(dev); + if (IS_ERR(channels)) + return PTR_ERR(channels); + + error = devm_add_action(dev, vf50_ts_channel_release, channels); + if (error) { + iio_channel_release_all(channels); + dev_err(dev, "Failed to register iio channel release action"); + return error; + } + + num_adc_channels = 0; + while (channels[num_adc_channels].indio_dev) + num_adc_channels++; + + if (num_adc_channels != COLI_TOUCH_REQ_ADC_CHAN) { + dev_err(dev, "Inadequate ADC channels specified\n"); + return -EINVAL; + } + + touchdev = devm_kzalloc(dev, sizeof(*touchdev), GFP_KERNEL); + if (!touchdev) + return -ENOMEM; + + touchdev->pdev = pdev; + touchdev->channels = channels; + + error = of_property_read_u32(dev->of_node, "vf50-ts-min-pressure", + &touchdev->min_pressure); + if (error) + return error; + + input = devm_input_allocate_device(dev); + if (!input) { + dev_err(dev, "Failed to allocate TS input device\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, touchdev); + + input->name = DRIVER_NAME; + input->id.bustype = BUS_HOST; + input->dev.parent = dev; + input->open = vf50_ts_open; + input->close = vf50_ts_close; + + input_set_capability(input, EV_KEY, BTN_TOUCH); + input_set_abs_params(input, ABS_X, 0, VF_ADC_MAX, 0, 0); + input_set_abs_params(input, ABS_Y, 0, VF_ADC_MAX, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, VF_ADC_MAX, 0, 0); + + touchdev->ts_input = input; + input_set_drvdata(input, touchdev); + + error = input_register_device(input); + if (error) { + dev_err(dev, "Failed to register input device\n"); + return error; + } + + error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xp, "xp", GPIOD_OUT_LOW); + if (error) + return error; + + error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xm, + "xm", GPIOD_OUT_LOW); + if (error) + return error; + + error = vf50_ts_get_gpiod(dev, &touchdev->gpio_yp, "yp", GPIOD_OUT_LOW); + if (error) + return error; + + error = vf50_ts_get_gpiod(dev, &touchdev->gpio_ym, "ym", GPIOD_OUT_LOW); + if (error) + return error; + + touchdev->pen_irq = platform_get_irq(pdev, 0); + if (touchdev->pen_irq < 0) + return touchdev->pen_irq; + + error = devm_request_threaded_irq(dev, touchdev->pen_irq, + NULL, vf50_ts_irq_bh, IRQF_ONESHOT, + "vf50 touch", touchdev); + if (error) { + dev_err(dev, "Failed to request IRQ %d: %d\n", + touchdev->pen_irq, error); + return error; + } + + return 0; +} + +static const struct of_device_id vf50_touch_of_match[] = { + { .compatible = "toradex,vf50-touchscreen", }, + { } +}; +MODULE_DEVICE_TABLE(of, vf50_touch_of_match); + +static struct platform_driver vf50_touch_driver = { + .driver = { + .name = "toradex,vf50_touchctrl", + .of_match_table = vf50_touch_of_match, + }, + .probe = vf50_ts_probe, +}; +module_platform_driver(vf50_touch_driver); + +MODULE_AUTHOR("Sanchayan Maity"); +MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c index 9a323dd915de..a9f95c7d3c00 100644 --- a/drivers/input/touchscreen/cyttsp4_i2c.c +++ b/drivers/input/touchscreen/cyttsp4_i2c.c @@ -86,4 +86,3 @@ module_i2c_driver(cyttsp4_i2c_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); MODULE_AUTHOR("Cypress"); -MODULE_ALIAS("i2c:cyttsp4"); diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c index 519e2de2f8df..eee51b3f2e3f 100644 --- a/drivers/input/touchscreen/cyttsp_i2c.c +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -86,4 +86,3 @@ module_i2c_driver(cyttsp_i2c_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); MODULE_AUTHOR("Cypress"); -MODULE_ALIAS("i2c:cyttsp"); diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index ddac134b25b1..17cc20ef4923 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -102,7 +102,7 @@ #define ELAN_FW_PAGESIZE 132 /* calibration timeout definition */ -#define ELAN_CALI_TIMEOUT_MSEC 10000 +#define ELAN_CALI_TIMEOUT_MSEC 12000 #define ELAN_POWERON_DELAY_USEC 500 #define ELAN_RESET_DELAY_MSEC 20 diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c new file mode 100644 index 000000000000..ff0b75813daa --- /dev/null +++ b/drivers/input/touchscreen/imx6ul_tsc.c @@ -0,0 +1,523 @@ +/* + * Freescale i.MX6UL touchscreen controller driver + * + * Copyright (C) 2015 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/gpio/consumer.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> + +/* ADC configuration registers field define */ +#define ADC_AIEN (0x1 << 7) +#define ADC_CONV_DISABLE 0x1F +#define ADC_CAL (0x1 << 7) +#define ADC_CALF 0x2 +#define ADC_12BIT_MODE (0x2 << 2) +#define ADC_IPG_CLK 0x00 +#define ADC_CLK_DIV_8 (0x03 << 5) +#define ADC_SHORT_SAMPLE_MODE (0x0 << 4) +#define ADC_HARDWARE_TRIGGER (0x1 << 13) +#define SELECT_CHANNEL_4 0x04 +#define SELECT_CHANNEL_1 0x01 +#define DISABLE_CONVERSION_INT (0x0 << 7) + +/* ADC registers */ +#define REG_ADC_HC0 0x00 +#define REG_ADC_HC1 0x04 +#define REG_ADC_HC2 0x08 +#define REG_ADC_HC3 0x0C +#define REG_ADC_HC4 0x10 +#define REG_ADC_HS 0x14 +#define REG_ADC_R0 0x18 +#define REG_ADC_CFG 0x2C +#define REG_ADC_GC 0x30 +#define REG_ADC_GS 0x34 + +#define ADC_TIMEOUT msecs_to_jiffies(100) + +/* TSC registers */ +#define REG_TSC_BASIC_SETING 0x00 +#define REG_TSC_PRE_CHARGE_TIME 0x10 +#define REG_TSC_FLOW_CONTROL 0x20 +#define REG_TSC_MEASURE_VALUE 0x30 +#define REG_TSC_INT_EN 0x40 +#define REG_TSC_INT_SIG_EN 0x50 +#define REG_TSC_INT_STATUS 0x60 +#define REG_TSC_DEBUG_MODE 0x70 +#define REG_TSC_DEBUG_MODE2 0x80 + +/* TSC configuration registers field define */ +#define DETECT_4_WIRE_MODE (0x0 << 4) +#define AUTO_MEASURE 0x1 +#define MEASURE_SIGNAL 0x1 +#define DETECT_SIGNAL (0x1 << 4) +#define VALID_SIGNAL (0x1 << 8) +#define MEASURE_INT_EN 0x1 +#define MEASURE_SIG_EN 0x1 +#define VALID_SIG_EN (0x1 << 8) +#define DE_GLITCH_2 (0x2 << 29) +#define START_SENSE (0x1 << 12) +#define TSC_DISABLE (0x1 << 16) +#define DETECT_MODE 0x2 + +struct imx6ul_tsc { + struct device *dev; + struct input_dev *input; + void __iomem *tsc_regs; + void __iomem *adc_regs; + struct clk *tsc_clk; + struct clk *adc_clk; + struct gpio_desc *xnur_gpio; + + int measure_delay_time; + int pre_charge_time; + + struct completion completion; +}; + +/* + * TSC module need ADC to get the measure value. So + * before config TSC, we should initialize ADC module. + */ +static void imx6ul_adc_init(struct imx6ul_tsc *tsc) +{ + int adc_hc = 0; + int adc_gc; + int adc_gs; + int adc_cfg; + int timeout; + + reinit_completion(&tsc->completion); + + adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); + adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK; + adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE; + adc_cfg &= ~ADC_HARDWARE_TRIGGER; + writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); + + /* enable calibration interrupt */ + adc_hc |= ADC_AIEN; + adc_hc |= ADC_CONV_DISABLE; + writel(adc_hc, tsc->adc_regs + REG_ADC_HC0); + + /* start ADC calibration */ + adc_gc = readl(tsc->adc_regs + REG_ADC_GC); + adc_gc |= ADC_CAL; + writel(adc_gc, tsc->adc_regs + REG_ADC_GC); + + timeout = wait_for_completion_timeout + (&tsc->completion, ADC_TIMEOUT); + if (timeout == 0) + dev_err(tsc->dev, "Timeout for adc calibration\n"); + + adc_gs = readl(tsc->adc_regs + REG_ADC_GS); + if (adc_gs & ADC_CALF) + dev_err(tsc->dev, "ADC calibration failed\n"); + + /* TSC need the ADC work in hardware trigger */ + adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG); + adc_cfg |= ADC_HARDWARE_TRIGGER; + writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG); +} + +/* + * This is a TSC workaround. Currently TSC misconnect two + * ADC channels, this function remap channel configure for + * hardware trigger. + */ +static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc) +{ + int adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4; + + adc_hc0 = DISABLE_CONVERSION_INT; + writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0); + + adc_hc1 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_4; + writel(adc_hc1, tsc->adc_regs + REG_ADC_HC1); + + adc_hc2 = DISABLE_CONVERSION_INT; + writel(adc_hc2, tsc->adc_regs + REG_ADC_HC2); + + adc_hc3 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_1; + writel(adc_hc3, tsc->adc_regs + REG_ADC_HC3); + + adc_hc4 = DISABLE_CONVERSION_INT; + writel(adc_hc4, tsc->adc_regs + REG_ADC_HC4); +} + +/* + * TSC setting, confige the pre-charge time and measure delay time. + * different touch screen may need different pre-charge time and + * measure delay time. + */ +static void imx6ul_tsc_set(struct imx6ul_tsc *tsc) +{ + int basic_setting = 0; + int start; + + basic_setting |= tsc->measure_delay_time << 8; + basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE; + writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETING); + + writel(DE_GLITCH_2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2); + + writel(tsc->pre_charge_time, tsc->tsc_regs + REG_TSC_PRE_CHARGE_TIME); + writel(MEASURE_INT_EN, tsc->tsc_regs + REG_TSC_INT_EN); + writel(MEASURE_SIG_EN | VALID_SIG_EN, + tsc->tsc_regs + REG_TSC_INT_SIG_EN); + + /* start sense detection */ + start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); + start |= START_SENSE; + start &= ~TSC_DISABLE; + writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); +} + +static void imx6ul_tsc_init(struct imx6ul_tsc *tsc) +{ + imx6ul_adc_init(tsc); + imx6ul_tsc_channel_config(tsc); + imx6ul_tsc_set(tsc); +} + +static void imx6ul_tsc_disable(struct imx6ul_tsc *tsc) +{ + int tsc_flow; + int adc_cfg; + + /* TSC controller enters to idle status */ + tsc_flow = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); + tsc_flow |= TSC_DISABLE; + writel(tsc_flow, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); + + /* ADC controller enters to stop mode */ + adc_cfg = readl(tsc->adc_regs + REG_ADC_HC0); + adc_cfg |= ADC_CONV_DISABLE; + writel(adc_cfg, tsc->adc_regs + REG_ADC_HC0); +} + +/* Delay some time (max 2ms), wait the pre-charge done. */ +static bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(2); + int state_machine; + int debug_mode2; + + do { + if (time_after(jiffies, timeout)) + return false; + + usleep_range(200, 400); + debug_mode2 = readl(tsc->tsc_regs + REG_TSC_DEBUG_MODE2); + state_machine = (debug_mode2 >> 20) & 0x7; + } while (state_machine != DETECT_MODE); + + usleep_range(200, 400); + return true; +} + +static irqreturn_t tsc_irq_fn(int irq, void *dev_id) +{ + struct imx6ul_tsc *tsc = dev_id; + int status; + int value; + int x, y; + int start; + + status = readl(tsc->tsc_regs + REG_TSC_INT_STATUS); + + /* write 1 to clear the bit measure-signal */ + writel(MEASURE_SIGNAL | DETECT_SIGNAL, + tsc->tsc_regs + REG_TSC_INT_STATUS); + + /* It's a HW self-clean bit. Set this bit and start sense detection */ + start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL); + start |= START_SENSE; + writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL); + + if (status & MEASURE_SIGNAL) { + value = readl(tsc->tsc_regs + REG_TSC_MEASURE_VALUE); + x = (value >> 16) & 0x0fff; + y = value & 0x0fff; + + /* + * In detect mode, we can get the xnur gpio value, + * otherwise assume contact is stiull active. + */ + if (!tsc_wait_detect_mode(tsc) || + gpiod_get_value_cansleep(tsc->xnur_gpio)) { + input_report_key(tsc->input, BTN_TOUCH, 1); + input_report_abs(tsc->input, ABS_X, x); + input_report_abs(tsc->input, ABS_Y, y); + } else { + input_report_key(tsc->input, BTN_TOUCH, 0); + } + + input_sync(tsc->input); + } + + return IRQ_HANDLED; +} + +static irqreturn_t adc_irq_fn(int irq, void *dev_id) +{ + struct imx6ul_tsc *tsc = dev_id; + int coco; + int value; + + coco = readl(tsc->adc_regs + REG_ADC_HS); + if (coco & 0x01) { + value = readl(tsc->adc_regs + REG_ADC_R0); + complete(&tsc->completion); + } + + return IRQ_HANDLED; +} + +static int imx6ul_tsc_open(struct input_dev *input_dev) +{ + struct imx6ul_tsc *tsc = input_get_drvdata(input_dev); + int err; + + err = clk_prepare_enable(tsc->adc_clk); + if (err) { + dev_err(tsc->dev, + "Could not prepare or enable the adc clock: %d\n", + err); + return err; + } + + err = clk_prepare_enable(tsc->tsc_clk); + if (err) { + dev_err(tsc->dev, + "Could not prepare or enable the tsc clock: %d\n", + err); + clk_disable_unprepare(tsc->adc_clk); + return err; + } + + imx6ul_tsc_init(tsc); + + return 0; +} + +static void imx6ul_tsc_close(struct input_dev *input_dev) +{ + struct imx6ul_tsc *tsc = input_get_drvdata(input_dev); + + imx6ul_tsc_disable(tsc); + + clk_disable_unprepare(tsc->tsc_clk); + clk_disable_unprepare(tsc->adc_clk); +} + +static int imx6ul_tsc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct imx6ul_tsc *tsc; + struct input_dev *input_dev; + struct resource *tsc_mem; + struct resource *adc_mem; + int err; + int tsc_irq; + int adc_irq; + + tsc = devm_kzalloc(&pdev->dev, sizeof(struct imx6ul_tsc), GFP_KERNEL); + if (!tsc) + return -ENOMEM; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; + + input_dev->name = "iMX6UL TouchScreen Controller"; + input_dev->id.bustype = BUS_HOST; + + input_dev->open = imx6ul_tsc_open; + input_dev->close = imx6ul_tsc_close; + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, 0, 0xFFF, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 0xFFF, 0, 0); + + input_set_drvdata(input_dev, tsc); + + tsc->dev = &pdev->dev; + tsc->input = input_dev; + init_completion(&tsc->completion); + + tsc->xnur_gpio = devm_gpiod_get(&pdev->dev, "xnur", GPIOD_IN); + if (IS_ERR(tsc->xnur_gpio)) { + err = PTR_ERR(tsc->xnur_gpio); + dev_err(&pdev->dev, + "failed to request GPIO tsc_X- (xnur): %d\n", err); + return err; + } + + tsc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tsc->tsc_regs = devm_ioremap_resource(&pdev->dev, tsc_mem); + if (IS_ERR(tsc->tsc_regs)) { + err = PTR_ERR(tsc->tsc_regs); + dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err); + return err; + } + + adc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + tsc->adc_regs = devm_ioremap_resource(&pdev->dev, adc_mem); + if (IS_ERR(tsc->adc_regs)) { + err = PTR_ERR(tsc->adc_regs); + dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err); + return err; + } + + tsc->tsc_clk = devm_clk_get(&pdev->dev, "tsc"); + if (IS_ERR(tsc->tsc_clk)) { + err = PTR_ERR(tsc->tsc_clk); + dev_err(&pdev->dev, "failed getting tsc clock: %d\n", err); + return err; + } + + tsc->adc_clk = devm_clk_get(&pdev->dev, "adc"); + if (IS_ERR(tsc->adc_clk)) { + err = PTR_ERR(tsc->adc_clk); + dev_err(&pdev->dev, "failed getting adc clock: %d\n", err); + return err; + } + + tsc_irq = platform_get_irq(pdev, 0); + if (tsc_irq < 0) { + dev_err(&pdev->dev, "no tsc irq resource?\n"); + return tsc_irq; + } + + adc_irq = platform_get_irq(pdev, 1); + if (adc_irq <= 0) { + dev_err(&pdev->dev, "no adc irq resource?\n"); + return adc_irq; + } + + err = devm_request_threaded_irq(tsc->dev, tsc_irq, + NULL, tsc_irq_fn, IRQF_ONESHOT, + dev_name(&pdev->dev), tsc); + if (err) { + dev_err(&pdev->dev, + "failed requesting tsc irq %d: %d\n", + tsc_irq, err); + return err; + } + + err = devm_request_irq(tsc->dev, adc_irq, adc_irq_fn, 0, + dev_name(&pdev->dev), tsc); + if (err) { + dev_err(&pdev->dev, + "failed requesting adc irq %d: %d\n", + adc_irq, err); + return err; + } + + err = of_property_read_u32(np, "measure-delay-time", + &tsc->measure_delay_time); + if (err) + tsc->measure_delay_time = 0xffff; + + err = of_property_read_u32(np, "pre-charge-time", + &tsc->pre_charge_time); + if (err) + tsc->pre_charge_time = 0xfff; + + err = input_register_device(tsc->input); + if (err) { + dev_err(&pdev->dev, + "failed to register input device: %d\n", err); + return err; + } + + platform_set_drvdata(pdev, tsc); + return 0; +} + +static int __maybe_unused imx6ul_tsc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); + struct input_dev *input_dev = tsc->input; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) { + imx6ul_tsc_disable(tsc); + + clk_disable_unprepare(tsc->tsc_clk); + clk_disable_unprepare(tsc->adc_clk); + } + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int __maybe_unused imx6ul_tsc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); + struct input_dev *input_dev = tsc->input; + int retval = 0; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) { + retval = clk_prepare_enable(tsc->adc_clk); + if (retval) + goto out; + + retval = clk_prepare_enable(tsc->tsc_clk); + if (retval) { + clk_disable_unprepare(tsc->adc_clk); + goto out; + } + + imx6ul_tsc_init(tsc); + } + +out: + mutex_unlock(&input_dev->mutex); + return retval; +} + +static SIMPLE_DEV_PM_OPS(imx6ul_tsc_pm_ops, + imx6ul_tsc_suspend, imx6ul_tsc_resume); + +static const struct of_device_id imx6ul_tsc_match[] = { + { .compatible = "fsl,imx6ul-tsc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx6ul_tsc_match); + +static struct platform_driver imx6ul_tsc_driver = { + .driver = { + .name = "imx6ul-tsc", + .of_match_table = imx6ul_tsc_match, + .pm = &imx6ul_tsc_pm_ops, + }, + .probe = imx6ul_tsc_probe, +}; +module_platform_driver(imx6ul_tsc_driver); + +MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>"); +MODULE_DESCRIPTION("Freescale i.MX6UL Touchscreen controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index c0116994067d..485794376ee5 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -191,7 +191,7 @@ static void sun4i_ts_close(struct input_dev *dev) writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC); } -static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp) +static int sun4i_get_temp(const struct sun4i_ts_data *ts, int *temp) { /* No temp_data until the first irq */ if (ts->temp_data == -1) @@ -202,7 +202,7 @@ static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp) return 0; } -static int sun4i_get_tz_temp(void *data, long *temp) +static int sun4i_get_tz_temp(void *data, int *temp) { return sun4i_get_temp(data, temp); } @@ -215,14 +215,14 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, char *buf) { struct sun4i_ts_data *ts = dev_get_drvdata(dev); - long temp; + int temp; int error; error = sun4i_get_temp(ts, &temp); if (error) return error; - return sprintf(buf, "%ld\n", temp); + return sprintf(buf, "%d\n", temp); } static ssize_t show_temp_label(struct device *dev, diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 4664c2a96c67..d9da766719c8 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -43,7 +43,7 @@ config IOMMU_IO_PGTABLE_LPAE_SELFTEST endmenu config IOMMU_IOVA - bool + tristate config OF_IOMMU def_bool y diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 2d7349a3ee14..041bc1810a86 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3215,6 +3215,8 @@ static struct iova *intel_alloc_iova(struct device *dev, /* Restrict dma_mask to the width that the iommu can handle */ dma_mask = min_t(uint64_t, DOMAIN_MAX_ADDR(domain->gaw), dma_mask); + /* Ensure we reserve the whole size-aligned region */ + nrpages = __roundup_pow_of_two(nrpages); if (!dmar_forcedac && dma_mask > DMA_BIT_MASK(32)) { /* @@ -3711,7 +3713,7 @@ static inline int iommu_devinfo_cache_init(void) static int __init iommu_init_mempool(void) { int ret; - ret = iommu_iova_cache_init(); + ret = iova_cache_get(); if (ret) return ret; @@ -3725,7 +3727,7 @@ static int __init iommu_init_mempool(void) kmem_cache_destroy(iommu_domain_cache); domain_error: - iommu_iova_cache_destroy(); + iova_cache_put(); return -ENOMEM; } @@ -3734,7 +3736,7 @@ static void __init iommu_exit_mempool(void) { kmem_cache_destroy(iommu_devinfo_cache); kmem_cache_destroy(iommu_domain_cache); - iommu_iova_cache_destroy(); + iova_cache_put(); } static void quirk_ioat_snb_local_iommu(struct pci_dev *pdev) diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index b7c3d923f3e1..fa0adef32bd6 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -18,42 +18,9 @@ */ #include <linux/iova.h> +#include <linux/module.h> #include <linux/slab.h> -static struct kmem_cache *iommu_iova_cache; - -int iommu_iova_cache_init(void) -{ - int ret = 0; - - iommu_iova_cache = kmem_cache_create("iommu_iova", - sizeof(struct iova), - 0, - SLAB_HWCACHE_ALIGN, - NULL); - if (!iommu_iova_cache) { - pr_err("Couldn't create iova cache\n"); - ret = -ENOMEM; - } - - return ret; -} - -void iommu_iova_cache_destroy(void) -{ - kmem_cache_destroy(iommu_iova_cache); -} - -struct iova *alloc_iova_mem(void) -{ - return kmem_cache_alloc(iommu_iova_cache, GFP_ATOMIC); -} - -void free_iova_mem(struct iova *iova) -{ - kmem_cache_free(iommu_iova_cache, iova); -} - void init_iova_domain(struct iova_domain *iovad, unsigned long granule, unsigned long start_pfn, unsigned long pfn_32bit) @@ -72,6 +39,7 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule, iovad->start_pfn = start_pfn; iovad->dma_32bit_pfn = pfn_32bit; } +EXPORT_SYMBOL_GPL(init_iova_domain); static struct rb_node * __get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn) @@ -120,19 +88,14 @@ __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free) } } -/* Computes the padding size required, to make the - * the start address naturally aligned on its size +/* + * Computes the padding size required, to make the start address + * naturally aligned on the power-of-two order of its size */ -static int -iova_get_pad_size(int size, unsigned int limit_pfn) +static unsigned int +iova_get_pad_size(unsigned int size, unsigned int limit_pfn) { - unsigned int pad_size = 0; - unsigned int order = ilog2(size); - - if (order) - pad_size = (limit_pfn + 1) % (1 << order); - - return pad_size; + return (limit_pfn + 1 - size) & (__roundup_pow_of_two(size) - 1); } static int __alloc_and_insert_iova_range(struct iova_domain *iovad, @@ -242,6 +205,57 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova) rb_insert_color(&iova->node, root); } +static struct kmem_cache *iova_cache; +static unsigned int iova_cache_users; +static DEFINE_MUTEX(iova_cache_mutex); + +struct iova *alloc_iova_mem(void) +{ + return kmem_cache_alloc(iova_cache, GFP_ATOMIC); +} +EXPORT_SYMBOL(alloc_iova_mem); + +void free_iova_mem(struct iova *iova) +{ + kmem_cache_free(iova_cache, iova); +} +EXPORT_SYMBOL(free_iova_mem); + +int iova_cache_get(void) +{ + mutex_lock(&iova_cache_mutex); + if (!iova_cache_users) { + iova_cache = kmem_cache_create( + "iommu_iova", sizeof(struct iova), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!iova_cache) { + mutex_unlock(&iova_cache_mutex); + printk(KERN_ERR "Couldn't create iova cache\n"); + return -ENOMEM; + } + } + + iova_cache_users++; + mutex_unlock(&iova_cache_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(iova_cache_get); + +void iova_cache_put(void) +{ + mutex_lock(&iova_cache_mutex); + if (WARN_ON(!iova_cache_users)) { + mutex_unlock(&iova_cache_mutex); + return; + } + iova_cache_users--; + if (!iova_cache_users) + kmem_cache_destroy(iova_cache); + mutex_unlock(&iova_cache_mutex); +} +EXPORT_SYMBOL_GPL(iova_cache_put); + /** * alloc_iova - allocates an iova * @iovad: - iova domain in question @@ -265,12 +279,6 @@ alloc_iova(struct iova_domain *iovad, unsigned long size, if (!new_iova) return NULL; - /* If size aligned is set then round the size to - * to next power of two. - */ - if (size_aligned) - size = __roundup_pow_of_two(size); - ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn, new_iova, size_aligned); @@ -281,6 +289,7 @@ alloc_iova(struct iova_domain *iovad, unsigned long size, return new_iova; } +EXPORT_SYMBOL_GPL(alloc_iova); /** * find_iova - find's an iova for a given pfn @@ -321,6 +330,7 @@ struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn) spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); return NULL; } +EXPORT_SYMBOL_GPL(find_iova); /** * __free_iova - frees the given iova @@ -339,6 +349,7 @@ __free_iova(struct iova_domain *iovad, struct iova *iova) spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); free_iova_mem(iova); } +EXPORT_SYMBOL_GPL(__free_iova); /** * free_iova - finds and frees the iova for a given pfn @@ -356,6 +367,7 @@ free_iova(struct iova_domain *iovad, unsigned long pfn) __free_iova(iovad, iova); } +EXPORT_SYMBOL_GPL(free_iova); /** * put_iova_domain - destroys the iova doamin @@ -378,6 +390,7 @@ void put_iova_domain(struct iova_domain *iovad) } spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); } +EXPORT_SYMBOL_GPL(put_iova_domain); static int __is_range_overlap(struct rb_node *node, @@ -467,6 +480,7 @@ finish: spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); return iova; } +EXPORT_SYMBOL_GPL(reserve_iova); /** * copy_reserved_iova - copies the reserved between domains @@ -493,6 +507,7 @@ copy_reserved_iova(struct iova_domain *from, struct iova_domain *to) } spin_unlock_irqrestore(&from->iova_rbtree_lock, flags); } +EXPORT_SYMBOL_GPL(copy_reserved_iova); struct iova * split_and_remove_iova(struct iova_domain *iovad, struct iova *iova, @@ -534,3 +549,6 @@ error: free_iova_mem(prev); return NULL; } + +MODULE_AUTHOR("Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iommu/omap-iommu-debug.c b/drivers/iommu/omap-iommu-debug.c index 0717aa96ce39..9bc20e2119a3 100644 --- a/drivers/iommu/omap-iommu-debug.c +++ b/drivers/iommu/omap-iommu-debug.c @@ -135,8 +135,9 @@ __dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num) static ssize_t iotlb_dump_cr(struct omap_iommu *obj, struct cr_regs *cr, struct seq_file *s) { - return seq_printf(s, "%08x %08x %01x\n", cr->cam, cr->ram, + seq_printf(s, "%08x %08x %01x\n", cr->cam, cr->ram, (cr->cam & MMU_CAM_P) ? 1 : 0); + return 0; } static size_t omap_dump_tlb_entries(struct omap_iommu *obj, struct seq_file *s) diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index e9c6f2a5b52d..cd7d3bc78e34 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -65,12 +65,10 @@ static void combiner_unmask_irq(struct irq_data *data) __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET); } -static void combiner_handle_cascade_irq(unsigned int __irq, - struct irq_desc *desc) +static void combiner_handle_cascade_irq(struct irq_desc *desc) { struct combiner_chip_data *chip_data = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); - unsigned int irq = irq_desc_get_irq(desc); unsigned int cascade_irq, combiner_irq; unsigned long status; @@ -88,7 +86,7 @@ static void combiner_handle_cascade_irq(unsigned int __irq, cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq); if (unlikely(!cascade_irq)) - handle_bad_irq(irq, desc); + handle_bad_irq(desc); else generic_handle_irq(cascade_irq); @@ -165,7 +163,7 @@ static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq); irq_set_chip_data(irq, &combiner_data[hw >> 3]); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + irq_set_probe(irq); return 0; } diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 39b72da0c143..9d89900a3f88 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -56,9 +56,6 @@ #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) -#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5) -#define ARMADA_370_XP_FABRIC_IRQ (3) - #define IPI_DOORBELL_START (0) #define IPI_DOORBELL_END (8) #define IPI_DOORBELL_MASK 0xFF @@ -81,13 +78,10 @@ static phys_addr_t msi_doorbell_addr; static inline bool is_percpu_irq(irq_hw_number_t irq) { - switch (irq) { - case ARMADA_370_XP_TIMER0_PER_CPU_IRQ: - case ARMADA_370_XP_FABRIC_IRQ: + if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS) return true; - default: - return false; - } + + return false; } /* @@ -200,7 +194,6 @@ static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq, { irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip, handle_simple_irq); - set_irq_flags(virq, IRQF_VALID); return 0; } @@ -317,7 +310,7 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h, irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, handle_level_irq); } - set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + irq_set_probe(virq); return 0; } @@ -447,8 +440,7 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {} #endif -static void armada_370_xp_mpic_handle_cascade_irq(unsigned int irq, - struct irq_desc *desc) +static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); unsigned long irqmap, irqn, irqsrc, cpuid; @@ -551,7 +543,7 @@ static void armada_370_xp_mpic_resume(void) if (virq == 0) continue; - if (irq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) + if (!is_percpu_irq(irq)) writel(irq, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); else diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index 9da9942ac83c..f6d680485bee 100644 --- a/drivers/irqchip/irq-atmel-aic5.c +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -88,28 +88,36 @@ static void aic5_mask(struct irq_data *d) { struct irq_domain *domain = d->domain; struct irq_domain_chip_generic *dgc = domain->gc; - struct irq_chip_generic *gc = dgc->gc[0]; + struct irq_chip_generic *bgc = dgc->gc[0]; + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - /* Disable interrupt on AIC5 */ - irq_gc_lock(gc); + /* + * Disable interrupt on AIC5. We always take the lock of the + * first irq chip as all chips share the same registers. + */ + irq_gc_lock(bgc); irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); irq_reg_writel(gc, 1, AT91_AIC5_IDCR); gc->mask_cache &= ~d->mask; - irq_gc_unlock(gc); + irq_gc_unlock(bgc); } static void aic5_unmask(struct irq_data *d) { struct irq_domain *domain = d->domain; struct irq_domain_chip_generic *dgc = domain->gc; - struct irq_chip_generic *gc = dgc->gc[0]; + struct irq_chip_generic *bgc = dgc->gc[0]; + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - /* Enable interrupt on AIC5 */ - irq_gc_lock(gc); + /* + * Enable interrupt on AIC5. We always take the lock of the + * first irq chip as all chips share the same registers. + */ + irq_gc_lock(bgc); irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); irq_reg_writel(gc, 1, AT91_AIC5_IECR); gc->mask_cache |= d->mask; - irq_gc_unlock(gc); + irq_gc_unlock(bgc); } static int aic5_retrigger(struct irq_data *d) diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c index ed4ca9deca70..bf9cc5f2e839 100644 --- a/drivers/irqchip/irq-bcm2835.c +++ b/drivers/irqchip/irq-bcm2835.c @@ -96,7 +96,7 @@ struct armctrl_ic { static struct armctrl_ic intc __read_mostly; static void __exception_irq_entry bcm2835_handle_irq( struct pt_regs *regs); -static void bcm2836_chained_handle_irq(unsigned int irq, struct irq_desc *desc); +static void bcm2836_chained_handle_irq(struct irq_desc *desc); static void armctrl_mask_irq(struct irq_data *d) { @@ -166,7 +166,7 @@ static int __init armctrl_of_init(struct device_node *node, BUG_ON(irq <= 0); irq_set_chip_and_handler(irq, &armctrl_chip, handle_level_irq); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + irq_set_probe(irq); } } @@ -245,7 +245,7 @@ static void __exception_irq_entry bcm2835_handle_irq( handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs); } -static void bcm2836_chained_handle_irq(unsigned int irq, struct irq_desc *desc) +static void bcm2836_chained_handle_irq(struct irq_desc *desc) { u32 hwirq; diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c index 409bdc6366c2..0fea985ef1dc 100644 --- a/drivers/irqchip/irq-bcm7038-l1.c +++ b/drivers/irqchip/irq-bcm7038-l1.c @@ -115,7 +115,7 @@ static inline void l1_writel(u32 val, void __iomem *reg) writel(val, reg); } -static void bcm7038_l1_irq_handle(unsigned int irq, struct irq_desc *desc) +static void bcm7038_l1_irq_handle(struct irq_desc *desc) { struct bcm7038_l1_chip *intc = irq_desc_get_handler_data(desc); struct bcm7038_l1_cpu *cpu; diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index d3f976913a6f..61b18ab33ad9 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -56,7 +56,7 @@ struct bcm7120_l2_intc_data { const __be32 *map_mask_prop; }; -static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) +static void bcm7120_l2_intc_irq_handle(struct irq_desc *desc) { struct bcm7120_l1_intc_data *data = irq_desc_get_handler_data(desc); struct bcm7120_l2_intc_data *b = data->b; diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index aedda06191eb..65cd341f331a 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c @@ -49,13 +49,12 @@ struct brcmstb_l2_intc_data { u32 saved_mask; /* for suspend/resume */ }; -static void brcmstb_l2_intc_irq_handle(unsigned int __irq, - struct irq_desc *desc) +static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc) { struct brcmstb_l2_intc_data *b = irq_desc_get_handler_data(desc); struct irq_chip_generic *gc = irq_get_domain_generic_chip(b->domain, 0); struct irq_chip *chip = irq_desc_get_chip(desc); - unsigned int irq = irq_desc_get_irq(desc); + unsigned int irq; u32 status; chained_irq_enter(chip, desc); @@ -65,7 +64,7 @@ static void brcmstb_l2_intc_irq_handle(unsigned int __irq, if (status == 0) { raw_spin_lock(&desc->lock); - handle_bad_irq(irq, desc); + handle_bad_irq(desc); raw_spin_unlock(&desc->lock); goto out; } diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c index 2dd929eed9e0..eb5eb0cd414d 100644 --- a/drivers/irqchip/irq-clps711x.c +++ b/drivers/irqchip/irq-clps711x.c @@ -132,14 +132,14 @@ static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { irq_flow_handler_t handler = handle_level_irq; - unsigned int flags = IRQF_VALID | IRQF_PROBE; + unsigned int flags = 0; if (!clps711x_irqs[hw].flags) return 0; if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) { handler = handle_bad_irq; - flags |= IRQF_NOAUTOEN; + flags |= IRQ_NOAUTOEN; } else if (clps711x_irqs[hw].eoi) { handler = handle_fasteoi_irq; } @@ -149,7 +149,7 @@ static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq, writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hw].eoi); irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler); - set_irq_flags(virq, flags); + irq_modify_status(virq, IRQ_NOPROBE, flags); return 0; } diff --git a/drivers/irqchip/irq-dw-apb-ictl.c b/drivers/irqchip/irq-dw-apb-ictl.c index efd95d9955e7..052f266364c0 100644 --- a/drivers/irqchip/irq-dw-apb-ictl.c +++ b/drivers/irqchip/irq-dw-apb-ictl.c @@ -26,7 +26,7 @@ #define APB_INT_FINALSTATUS_H 0x34 #define APB_INT_BASE_OFFSET 0x04 -static void dw_apb_ictl_handler(unsigned int irq, struct irq_desc *desc) +static void dw_apb_ictl_handler(struct irq_desc *desc) { struct irq_domain *d = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index db04fc1f56b2..12985daa66ab 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -95,8 +95,8 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) struct v2m_data *v2m = irq_data_get_irq_chip_data(data); phys_addr_t addr = v2m->res.start + V2M_MSI_SETSPI_NS; - msg->address_hi = (u32) (addr >> 32); - msg->address_lo = (u32) (addr); + msg->address_hi = upper_32_bits(addr); + msg->address_lo = lower_32_bits(addr); msg->data = data->hwirq; } diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 26b55c53755f..ac7ae2b3cb83 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -898,8 +898,10 @@ retry_baser: * non-cacheable as well. */ shr = tmp & GITS_BASER_SHAREABILITY_MASK; - if (!shr) + if (!shr) { cache = GITS_BASER_nC; + __flush_dcache_area(base, alloc_size); + } goto retry_baser; } @@ -1140,6 +1142,8 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, return NULL; } + __flush_dcache_area(itt, sz); + dev->its = its; dev->itt = itt; dev->nr_ites = nr_ites; diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 7deed6ef54c2..36ecfc870e5a 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -70,11 +70,6 @@ static inline int gic_irq_in_rdist(struct irq_data *d) return gic_irq(d) < 32; } -static inline bool forwarded_irq(struct irq_data *d) -{ - return d->handler_data != NULL; -} - static inline void __iomem *gic_dist_base(struct irq_data *d) { if (gic_irq_in_rdist(d)) /* SGI+PPI -> SGI_base for this CPU */ @@ -249,7 +244,7 @@ static void gic_eoimode1_mask_irq(struct irq_data *d) * disabled/masked will not get "stuck", because there is * noone to deactivate it (guest is being terminated). */ - if (forwarded_irq(d)) + if (irqd_is_forwarded_to_vcpu(d)) gic_poke_irq(d, GICD_ICACTIVER); } @@ -324,7 +319,7 @@ static void gic_eoimode1_eoi_irq(struct irq_data *d) * No need to deactivate an LPI, or an interrupt that * is is getting forwarded to a vcpu. */ - if (gic_irq(d) >= 8192 || forwarded_irq(d)) + if (gic_irq(d) >= 8192 || irqd_is_forwarded_to_vcpu(d)) return; gic_write_dir(gic_irq(d)); } @@ -357,7 +352,10 @@ static int gic_set_type(struct irq_data *d, unsigned int type) static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu) { - d->handler_data = vcpu; + if (vcpu) + irqd_set_forwarded_to_vcpu(d); + else + irqd_clr_forwarded_to_vcpu(d); return 0; } @@ -754,13 +752,13 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_set_percpu_devid(irq); irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_percpu_devid_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); + irq_set_status_flags(irq, IRQ_NOAUTOEN); } /* SPIs */ if (hw >= 32 && hw < gic_data.irq_nr) { irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_fasteoi_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + irq_set_probe(irq); } /* LPIs */ if (hw >= 8192 && hw < GIC_ID_NR) { @@ -768,7 +766,6 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, return -EPERM; irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_fasteoi_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID); } return 0; diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index e6b7ed537952..982c09c2d791 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -145,29 +145,10 @@ static inline bool cascading_gic_irq(struct irq_data *d) void *data = irq_data_get_irq_handler_data(d); /* - * If handler_data pointing to one of the secondary GICs, then - * this is a cascading interrupt, and it cannot possibly be - * forwarded. + * If handler_data is set, this is a cascading interrupt, and + * it cannot possibly be forwarded. */ - if (data >= (void *)(gic_data + 1) && - data < (void *)(gic_data + MAX_GIC_NR)) - return true; - - return false; -} - -static inline bool forwarded_irq(struct irq_data *d) -{ - /* - * A forwarded interrupt: - * - is on the primary GIC - * - has its handler_data set to a value - * - that isn't a secondary GIC - */ - if (d->handler_data && !cascading_gic_irq(d)) - return true; - - return false; + return data != NULL; } /* @@ -201,7 +182,7 @@ static void gic_eoimode1_mask_irq(struct irq_data *d) * disabled/masked will not get "stuck", because there is * noone to deactivate it (guest is being terminated). */ - if (forwarded_irq(d)) + if (irqd_is_forwarded_to_vcpu(d)) gic_poke_irq(d, GIC_DIST_ACTIVE_CLEAR); } @@ -218,7 +199,7 @@ static void gic_eoi_irq(struct irq_data *d) static void gic_eoimode1_eoi_irq(struct irq_data *d) { /* Do not deactivate an IRQ forwarded to a vcpu. */ - if (forwarded_irq(d)) + if (irqd_is_forwarded_to_vcpu(d)) return; writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_DEACTIVATE); @@ -296,7 +277,10 @@ static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu) if (cascading_gic_irq(d)) return -EINVAL; - d->handler_data = vcpu; + if (vcpu) + irqd_set_forwarded_to_vcpu(d); + else + irqd_clr_forwarded_to_vcpu(d); return 0; } @@ -357,7 +341,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) } while (1); } -static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) +static void gic_handle_cascade_irq(struct irq_desc *desc) { struct gic_chip_data *chip_data = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); @@ -376,7 +360,7 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) cascade_irq = irq_find_mapping(chip_data->domain, gic_irq); if (unlikely(gic_irq < 32 || gic_irq > 1020)) - handle_bad_irq(cascade_irq, desc); + handle_bad_irq(desc); else generic_handle_irq(cascade_irq); @@ -906,11 +890,11 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_set_percpu_devid(irq); irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_percpu_devid_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); + irq_set_status_flags(irq, IRQ_NOAUTOEN); } else { irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_fasteoi_irq, NULL, NULL); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + irq_set_probe(irq); } return 0; } @@ -1119,12 +1103,49 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, #ifdef CONFIG_OF static int gic_cnt __initdata; +static bool gic_check_eoimode(struct device_node *node, void __iomem **base) +{ + struct resource cpuif_res; + + of_address_to_resource(node, 1, &cpuif_res); + + if (!is_hyp_mode_available()) + return false; + if (resource_size(&cpuif_res) < SZ_8K) + return false; + if (resource_size(&cpuif_res) == SZ_128K) { + u32 val_low, val_high; + + /* + * Verify that we have the first 4kB of a GIC400 + * aliased over the first 64kB by checking the + * GICC_IIDR register on both ends. + */ + val_low = readl_relaxed(*base + GIC_CPU_IDENT); + val_high = readl_relaxed(*base + GIC_CPU_IDENT + 0xf000); + if ((val_low & 0xffff0fff) != 0x0202043B || + val_low != val_high) + return false; + + /* + * Move the base up by 60kB, so that we have a 8kB + * contiguous region, which allows us to use GICC_DIR + * at its normal offset. Please pass me that bucket. + */ + *base += 0xf000; + cpuif_res.start += 0xf000; + pr_warn("GIC: Adjusting CPU interface base to %pa", + &cpuif_res.start); + } + + return true; +} + static int __init gic_of_init(struct device_node *node, struct device_node *parent) { void __iomem *cpu_base; void __iomem *dist_base; - struct resource cpu_res; u32 percpu_offset; int irq; @@ -1137,14 +1158,11 @@ gic_of_init(struct device_node *node, struct device_node *parent) cpu_base = of_iomap(node, 1); WARN(!cpu_base, "unable to map gic cpu registers\n"); - of_address_to_resource(node, 1, &cpu_res); - /* * Disable split EOI/Deactivate if either HYP is not available * or the CPU interface is too small. */ - if (gic_cnt == 0 && (!is_hyp_mode_available() || - resource_size(&cpu_res) < SZ_8K)) + if (gic_cnt == 0 && !gic_check_eoimode(node, &cpu_base)) static_key_slow_dec(&supports_deactivate); if (of_property_read_u32(node, "cpu-offset", &percpu_offset)) diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c index a0128c7c98dd..8f3ca8f3a62b 100644 --- a/drivers/irqchip/irq-hip04.c +++ b/drivers/irqchip/irq-hip04.c @@ -307,11 +307,11 @@ static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_set_percpu_devid(irq); irq_set_chip_and_handler(irq, &hip04_irq_chip, handle_percpu_devid_irq); - set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); + irq_set_status_flags(irq, IRQ_NOAUTOEN); } else { irq_set_chip_and_handler(irq, &hip04_irq_chip, handle_fasteoi_irq); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + irq_set_probe(irq); } irq_set_chip_data(irq, d->host_data); return 0; diff --git a/drivers/irqchip/irq-i8259.c b/drivers/irqchip/irq-i8259.c index 4836102ba312..e484fd255321 100644 --- a/drivers/irqchip/irq-i8259.c +++ b/drivers/irqchip/irq-i8259.c @@ -352,7 +352,7 @@ void __init init_i8259_irqs(void) __init_i8259_irqs(NULL); } -static void i8259_irq_dispatch(unsigned int __irq, struct irq_desc *desc) +static void i8259_irq_dispatch(struct irq_desc *desc) { struct irq_domain *domain = irq_desc_get_handler_data(desc); int hwirq = i8259_irq(); diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c index 841604b81004..c02d29c9dc05 100644 --- a/drivers/irqchip/irq-imgpdc.c +++ b/drivers/irqchip/irq-imgpdc.c @@ -218,7 +218,7 @@ static int pdc_irq_set_wake(struct irq_data *data, unsigned int on) return 0; } -static void pdc_intc_perip_isr(unsigned int __irq, struct irq_desc *desc) +static void pdc_intc_perip_isr(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct pdc_intc_priv *priv; @@ -240,7 +240,7 @@ found: generic_handle_irq(irq_no); } -static void pdc_intc_syswake_isr(unsigned int irq, struct irq_desc *desc) +static void pdc_intc_syswake_isr(struct irq_desc *desc) { struct pdc_intc_priv *priv; unsigned int syswake, irq_no; diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c index c1517267b5db..deb89d63a728 100644 --- a/drivers/irqchip/irq-keystone.c +++ b/drivers/irqchip/irq-keystone.c @@ -83,7 +83,7 @@ static void keystone_irq_ack(struct irq_data *d) /* nothing to do here */ } -static void keystone_irq_handler(unsigned __irq, struct irq_desc *desc) +static void keystone_irq_handler(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct keystone_irq_device *kirq = irq_desc_get_handler_data(desc); @@ -127,7 +127,7 @@ static int keystone_irq_map(struct irq_domain *h, unsigned int virq, irq_set_chip_data(virq, kirq); irq_set_chip_and_handler(virq, &kirq->chip, handle_level_irq); - set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + irq_set_probe(virq); return 0; } diff --git a/drivers/irqchip/irq-metag-ext.c b/drivers/irqchip/irq-metag-ext.c index 5f4c52928d16..8c38b3d92e1c 100644 --- a/drivers/irqchip/irq-metag-ext.c +++ b/drivers/irqchip/irq-metag-ext.c @@ -446,7 +446,7 @@ static int meta_intc_irq_set_type(struct irq_data *data, unsigned int flow_type) * Whilst using TR2 to detect external interrupts is a software convention it is * (hopefully) unlikely to change. */ -static void meta_intc_irq_demux(unsigned int irq, struct irq_desc *desc) +static void meta_intc_irq_demux(struct irq_desc *desc) { struct meta_intc_priv *priv = &meta_intc_priv; irq_hw_number_t hw; diff --git a/drivers/irqchip/irq-metag.c b/drivers/irqchip/irq-metag.c index 3d23ce3edb5c..a5f053bd2f44 100644 --- a/drivers/irqchip/irq-metag.c +++ b/drivers/irqchip/irq-metag.c @@ -220,7 +220,7 @@ static int metag_internal_irq_set_affinity(struct irq_data *data, * occurred. It is this function's job to demux this irq and * figure out exactly which trigger needs servicing. */ -static void metag_internal_irq_demux(unsigned int irq, struct irq_desc *desc) +static void metag_internal_irq_demux(struct irq_desc *desc) { struct metag_internal_irq_priv *priv = irq_desc_get_handler_data(desc); irq_hw_number_t hw; diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index 1764bcf8ee6b..aeaa061f0dbf 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -320,6 +320,14 @@ static void gic_handle_shared_int(bool chained) intrmask[i] = gic_read(intrmask_reg); pending_reg += gic_reg_step; intrmask_reg += gic_reg_step; + + if (!config_enabled(CONFIG_64BIT) || mips_cm_is64) + continue; + + pending[i] |= (u64)gic_read(pending_reg) << 32; + intrmask[i] |= (u64)gic_read(intrmask_reg) << 32; + pending_reg += gic_reg_step; + intrmask_reg += gic_reg_step; } bitmap_and(pending, pending, intrmask, gic_shared_intrs); @@ -426,7 +434,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, spin_lock_irqsave(&gic_lock, flags); /* Re-route this IRQ */ - gic_map_to_vpe(irq, cpumask_first(&tmp)); + gic_map_to_vpe(irq, mips_cm_vp_id(cpumask_first(&tmp))); /* Update the pcpu_masks */ for (i = 0; i < NR_CPUS; i++) @@ -546,7 +554,7 @@ static void __gic_irq_dispatch(void) gic_handle_shared_int(false); } -static void gic_irq_dispatch(unsigned int irq, struct irq_desc *desc) +static void gic_irq_dispatch(struct irq_desc *desc) { gic_handle_local_int(true); gic_handle_shared_int(true); @@ -599,7 +607,7 @@ static __init void gic_ipi_init_one(unsigned int intr, int cpu, GIC_SHARED_TO_HWIRQ(intr)); int i; - gic_map_to_vpe(intr, cpu); + gic_map_to_vpe(intr, mips_cm_vp_id(cpu)); for (i = 0; i < NR_CPUS; i++) clear_bit(intr, pcpu_masks[i].pcpu_mask); set_bit(intr, pcpu_masks[cpu].pcpu_mask); diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c index 781ed6e71dbb..013fc9659a84 100644 --- a/drivers/irqchip/irq-mmp.c +++ b/drivers/irqchip/irq-mmp.c @@ -129,7 +129,7 @@ struct irq_chip icu_irq_chip = { .irq_unmask = icu_unmask_irq, }; -static void icu_mux_irq_demux(unsigned int __irq, struct irq_desc *desc) +static void icu_mux_irq_demux(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct irq_domain *domain; @@ -164,7 +164,6 @@ static int mmp_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq); - set_irq_flags(irq, IRQF_VALID); return 0; } @@ -234,7 +233,6 @@ void __init icu_init_irq(void) for (irq = 0; irq < 64; irq++) { icu_mask_irq(irq_get_irq_data(irq)); irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq); - set_irq_flags(irq, IRQF_VALID); } irq_set_default_host(icu_data[0].domain); set_handle_irq(mmp_handle_irq); @@ -337,7 +335,6 @@ void __init mmp2_init_icu(void) irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq); } - set_irq_flags(irq, IRQF_VALID); } irq_set_default_host(icu_data[0].domain); set_handle_irq(mmp2_handle_irq); diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c index 1faf812f3dc8..604df63e2edf 100644 --- a/drivers/irqchip/irq-mxs.c +++ b/drivers/irqchip/irq-mxs.c @@ -84,7 +84,6 @@ static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) { irq_set_chip_and_handler(virq, &mxs_icoll_chip, handle_level_irq); - set_irq_flags(virq, IRQF_VALID); return 0; } diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c index 5ea999a724b5..be4c5a8c9659 100644 --- a/drivers/irqchip/irq-orion.c +++ b/drivers/irqchip/irq-orion.c @@ -106,7 +106,7 @@ IRQCHIP_DECLARE(orion_intc, "marvell,orion-intc", orion_irq_init); #define ORION_BRIDGE_IRQ_CAUSE 0x00 #define ORION_BRIDGE_IRQ_MASK 0x04 -static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc) +static void orion_bridge_irq_handler(struct irq_desc *desc) { struct irq_domain *d = irq_desc_get_handler_data(desc); diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 0670ab4e3897..9525335723f6 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -283,6 +283,9 @@ static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type) static int intc_irqpin_irq_set_wake(struct irq_data *d, unsigned int on) { struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + irq_set_irq_wake(p->irq[hw_irq].requested_irq, on); if (!p->clk) return 0; @@ -332,6 +335,12 @@ static irqreturn_t intc_irqpin_shared_irq_handler(int irq, void *dev_id) return status; } +/* + * This lock class tells lockdep that INTC External IRQ Pin irqs are in a + * different category than their parents, so it won't report false recursion. + */ +static struct lock_class_key intc_irqpin_irq_lock_class; + static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { @@ -342,8 +351,8 @@ static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, intc_irqpin_dbg(&p->irq[hw], "map"); irq_set_chip_data(virq, h->host_data); + irq_set_lockdep_class(virq, &intc_irqpin_irq_lock_class); irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); - set_irq_flags(virq, IRQF_VALID); /* kill me now */ return 0; } diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c index 2aa3add711a6..35bf97ba4a3d 100644 --- a/drivers/irqchip/irq-renesas-irqc.c +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -121,6 +121,9 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type) static int irqc_irq_set_wake(struct irq_data *d, unsigned int on) { struct irqc_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + irq_set_irq_wake(p->irq[hw_irq].requested_irq, on); if (!p->clk) return 0; @@ -150,6 +153,12 @@ static irqreturn_t irqc_irq_handler(int irq, void *dev_id) return IRQ_NONE; } +/* + * This lock class tells lockdep that IRQC irqs are in a different + * category than their parents, so it won't report false recursion. + */ +static struct lock_class_key irqc_irq_lock_class; + static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { @@ -157,6 +166,7 @@ static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq, irqc_dbg(&p->irq[hw], "map"); irq_set_chip_data(virq, h->host_data); + irq_set_lockdep_class(virq, &irqc_irq_lock_class); irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); return 0; } diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 506d9f20ca51..7154b011ddd2 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -298,7 +298,7 @@ static struct irq_chip s3c_irq_eint0t4 = { .irq_set_type = s3c_irqext0_type, }; -static void s3c_irq_demux(unsigned int __irq, struct irq_desc *desc) +static void s3c_irq_demux(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc); @@ -466,13 +466,11 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, irq_set_chip_data(virq, irq_data); - set_irq_flags(virq, IRQF_VALID); - if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) { if (irq_data->parent_irq > 31) { pr_err("irq-s3c24xx: parent irq %lu is out of range\n", irq_data->parent_irq); - goto err; + return -EINVAL; } parent_irq_data = &parent_intc->irqs[irq_data->parent_irq]; @@ -485,18 +483,12 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, if (!irqno) { pr_err("irq-s3c24xx: could not find mapping for parent irq %lu\n", irq_data->parent_irq); - goto err; + return -EINVAL; } irq_set_chained_handler(irqno, s3c_irq_demux); } return 0; - -err: - set_irq_flags(virq, 0); - - /* the only error can result from bad mapping data*/ - return -EINVAL; } static const struct irq_domain_ops s3c24xx_irq_ops = { @@ -1174,8 +1166,6 @@ static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq, irq_set_chip_data(virq, irq_data); - set_irq_flags(virq, IRQF_VALID); - return 0; } diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index 4ad3e7c69aa7..0704362f4c82 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -83,7 +83,7 @@ static int sun4i_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) { irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq); - set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + irq_set_probe(virq); return 0; } diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c index 772a82cacbf7..c143dd58410c 100644 --- a/drivers/irqchip/irq-sunxi-nmi.c +++ b/drivers/irqchip/irq-sunxi-nmi.c @@ -58,7 +58,7 @@ static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off) return irq_reg_readl(gc, off); } -static void sunxi_sc_nmi_handle_irq(unsigned int irq, struct irq_desc *desc) +static void sunxi_sc_nmi_handle_irq(struct irq_desc *desc) { struct irq_domain *domain = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); diff --git a/drivers/irqchip/irq-tb10x.c b/drivers/irqchip/irq-tb10x.c index 331829661366..848d782a2a3b 100644 --- a/drivers/irqchip/irq-tb10x.c +++ b/drivers/irqchip/irq-tb10x.c @@ -97,7 +97,7 @@ static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) return IRQ_SET_MASK_OK; } -static void tb10x_irq_cascade(unsigned int __irq, struct irq_desc *desc) +static void tb10x_irq_cascade(struct irq_desc *desc) { struct irq_domain *domain = irq_desc_get_handler_data(desc); unsigned int irq = irq_desc_get_irq(desc); diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c index 16123f688768..598ab3f0e0ac 100644 --- a/drivers/irqchip/irq-versatile-fpga.c +++ b/drivers/irqchip/irq-versatile-fpga.c @@ -65,19 +65,19 @@ static void fpga_irq_unmask(struct irq_data *d) writel(mask, f->base + IRQ_ENABLE_SET); } -static void fpga_irq_handle(unsigned int __irq, struct irq_desc *desc) +static void fpga_irq_handle(struct irq_desc *desc) { struct fpga_irq_data *f = irq_desc_get_handler_data(desc); - unsigned int irq = irq_desc_get_irq(desc); u32 status = readl(f->base + IRQ_STATUS); if (status == 0) { - do_bad_IRQ(irq, desc); + do_bad_IRQ(desc); return; } do { - irq = ffs(status) - 1; + unsigned int irq = ffs(status) - 1; + status &= ~(1 << irq); generic_handle_irq(irq_find_mapping(f->domain, irq)); } while (status); @@ -128,7 +128,7 @@ static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq, irq_set_chip_data(irq, f); irq_set_chip_and_handler(irq, &f->chip, handle_level_irq); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + irq_set_probe(irq); return 0; } diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c index 03846dff4212..b956dfffe78c 100644 --- a/drivers/irqchip/irq-vic.c +++ b/drivers/irqchip/irq-vic.c @@ -201,7 +201,7 @@ static int vic_irqdomain_map(struct irq_domain *d, unsigned int irq, return -EPERM; irq_set_chip_and_handler(irq, &vic_chip, handle_level_irq); irq_set_chip_data(irq, v->base); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + irq_set_probe(irq); return 0; } @@ -225,7 +225,7 @@ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs) return handled; } -static void vic_handle_irq_cascaded(unsigned int irq, struct irq_desc *desc) +static void vic_handle_irq_cascaded(struct irq_desc *desc) { u32 stat, hwirq; struct irq_chip *host_chip = irq_desc_get_chip(desc); diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c index 8371d9978d31..f9af0af21751 100644 --- a/drivers/irqchip/irq-vt8500.c +++ b/drivers/irqchip/irq-vt8500.c @@ -167,7 +167,6 @@ static int vt8500_irq_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { irq_set_chip_and_handler(virq, &vt8500_irq_chip, handle_level_irq); - set_irq_flags(virq, IRQF_VALID); return 0; } diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 4cbd9c5dc1e6..1ccd2abed65f 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -182,7 +182,7 @@ static struct spear_shirq *spear320_shirq_blocks[] = { &spear320_shirq_intrcomm_ras, }; -static void shirq_handler(unsigned __irq, struct irq_desc *desc) +static void shirq_handler(struct irq_desc *desc) { struct spear_shirq *shirq = irq_desc_get_handler_data(desc); u32 pend; @@ -211,7 +211,6 @@ static void __init spear_shirq_register(struct spear_shirq *shirq, for (i = 0; i < shirq->nr_irqs; i++) { irq_set_chip_and_handler(shirq->virq_base + i, shirq->irq_chip, handle_simple_irq); - set_irq_flags(shirq->virq_base + i, IRQF_VALID); irq_set_chip_data(shirq->virq_base + i, shirq); } } diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c index 0e5d673871c0..9600cd771f1a 100644 --- a/drivers/isdn/hisax/hfc4s8s_l1.c +++ b/drivers/isdn/hisax/hfc4s8s_l1.c @@ -646,14 +646,14 @@ rx_d_frame(struct hfc4s8s_l1 *l1p, int ech) f1 = Read_hfc8_stable(l1p->hw, A_F1); f2 = Read_hfc8(l1p->hw, A_F2); - df = f1 - f2; - if ((f1 - f2) < 0) - df = f1 - f2 + MAX_F_CNT + 1; + if (f1 < f2) + df = MAX_F_CNT + 1 + f1 - f2; + else + df = f1 - f2; - if (!df) { + if (!df) return; /* no complete frame in fifo */ - } z1 = Read_hfc16_stable(l1p->hw, A_Z1); z2 = Read_hfc16(l1p->hw, A_Z2); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 70f4255ff291..42990f2d0317 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -170,6 +170,7 @@ config LEDS_SUNFIRE config LEDS_IPAQ_MICRO tristate "LED Support for the Compaq iPAQ h3xxx" + depends on LEDS_CLASS depends on MFD_IPAQ_MICRO help Choose this option if you want to use the notification LED on @@ -229,7 +230,7 @@ config LEDS_LP55XX_COMMON tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501" depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501 select FW_LOADER - select FW_LOADER_USER_HELPER_FALLBACK + select FW_LOADER_USER_HELPER help This option supports common operations for LP5521/5523/55231/5562/8501 devices. diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c index fd7c25fd29c1..ac77d36b630c 100644 --- a/drivers/leds/leds-aat1290.c +++ b/drivers/leds/leds-aat1290.c @@ -331,7 +331,7 @@ static void aat1290_led_validate_mm_current(struct aat1290_led *led, cfg->max_brightness = b + 1; } -int init_mm_current_scale(struct aat1290_led *led, +static int init_mm_current_scale(struct aat1290_led *led, struct aat1290_led_config_data *cfg) { int max_mm_current_percent[] = { 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, @@ -559,6 +559,7 @@ static const struct of_device_id aat1290_led_dt_match[] = { { .compatible = "skyworks,aat1290" }, {}, }; +MODULE_DEVICE_TABLE(of, aat1290_led_dt_match); static struct platform_driver aat1290_led_driver = { .probe = aat1290_led_probe, diff --git a/drivers/leds/leds-bcm6328.c b/drivers/leds/leds-bcm6328.c index 986fe1e28f84..1793727bc9ae 100644 --- a/drivers/leds/leds-bcm6328.c +++ b/drivers/leds/leds-bcm6328.c @@ -395,6 +395,7 @@ static const struct of_device_id bcm6328_leds_of_match[] = { { .compatible = "brcm,bcm6328-leds", }, { }, }; +MODULE_DEVICE_TABLE(of, bcm6328_leds_of_match); static struct platform_driver bcm6328_leds_driver = { .probe = bcm6328_leds_probe, diff --git a/drivers/leds/leds-bcm6358.c b/drivers/leds/leds-bcm6358.c index 21f96930b3be..7ea3526702e0 100644 --- a/drivers/leds/leds-bcm6358.c +++ b/drivers/leds/leds-bcm6358.c @@ -226,6 +226,7 @@ static const struct of_device_id bcm6358_leds_of_match[] = { { .compatible = "brcm,bcm6358-leds", }, { }, }; +MODULE_DEVICE_TABLE(of, bcm6358_leds_of_match); static struct platform_driver bcm6358_leds_driver = { .probe = bcm6358_leds_probe, diff --git a/drivers/leds/leds-ktd2692.c b/drivers/leds/leds-ktd2692.c index 2ae8c4d17ff8..feca07be85f5 100644 --- a/drivers/leds/leds-ktd2692.c +++ b/drivers/leds/leds-ktd2692.c @@ -426,6 +426,7 @@ static const struct of_device_id ktd2692_match[] = { { .compatible = "kinetic,ktd2692", }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, ktd2692_match); static struct platform_driver ktd2692_driver = { .driver = { diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c index df348a06d8c7..afbb1409b2e2 100644 --- a/drivers/leds/leds-max77693.c +++ b/drivers/leds/leds-max77693.c @@ -1080,6 +1080,7 @@ static const struct of_device_id max77693_led_dt_match[] = { { .compatible = "maxim,max77693-led" }, {}, }; +MODULE_DEVICE_TABLE(of, max77693_led_dt_match); static struct platform_driver max77693_led_driver = { .probe = max77693_led_probe, diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index b33514d9f427..a95a61220169 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -337,6 +337,7 @@ static const struct of_device_id of_ns2_leds_match[] = { { .compatible = "lacie,ns2-leds", }, {}, }; +MODULE_DEVICE_TABLE(of, of_ns2_leds_match); #endif /* CONFIG_OF_GPIO */ struct ns2_led_priv { diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index d5415eedba86..3e01e6fb3424 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -393,7 +393,7 @@ config DM_MULTIPATH # of SCSI_DH if the latter isn't defined but if # it is, DM_MULTIPATH must depend on it. We get a build # error if SCSI_DH=m and DM_MULTIPATH=y - depends on SCSI_DH || !SCSI_DH + depends on !SCSI_DH || SCSI ---help--- Allow volume managers to support multipath hardware. diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index d60c88df5234..4b3b6f8aff0c 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -968,7 +968,8 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone); /* * Generate a new unfragmented bio with the given size - * This should never violate the device limitations + * This should never violate the device limitations (but only because + * max_segment_size is being constrained to PAGE_SIZE). * * This function may be called concurrently. If we allocate from the mempool * concurrently, there is a possibility of deadlock. For example, if we have @@ -2045,9 +2046,20 @@ static int crypt_iterate_devices(struct dm_target *ti, return fn(ti, cc->dev, cc->start, ti->len, data); } +static void crypt_io_hints(struct dm_target *ti, struct queue_limits *limits) +{ + /* + * Unfortunate constraint that is required to avoid the potential + * for exceeding underlying device's max_segments limits -- due to + * crypt_alloc_buffer() possibly allocating pages for the encryption + * bio that are not as physically contiguous as the original bio. + */ + limits->max_segment_size = PAGE_SIZE; +} + static struct target_type crypt_target = { .name = "crypt", - .version = {1, 14, 0}, + .version = {1, 14, 1}, .module = THIS_MODULE, .ctr = crypt_ctr, .dtr = crypt_dtr, @@ -2058,6 +2070,7 @@ static struct target_type crypt_target = { .resume = crypt_resume, .message = crypt_message, .iterate_devices = crypt_iterate_devices, + .io_hints = crypt_io_hints, }; static int __init dm_crypt_init(void) diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index eff7bdd7731d..5a67671a3973 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -159,12 +159,9 @@ static struct priority_group *alloc_priority_group(void) static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti) { struct pgpath *pgpath, *tmp; - struct multipath *m = ti->private; list_for_each_entry_safe(pgpath, tmp, pgpaths, list) { list_del(&pgpath->list); - if (m->hw_handler_name) - scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev)); dm_put_device(ti, pgpath->path.dev); free_pgpath(pgpath); } @@ -580,6 +577,7 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps q = bdev_get_queue(p->path.dev->bdev); if (m->retain_attached_hw_handler) { +retain: attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL); if (attached_handler_name) { /* @@ -599,20 +597,14 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps } if (m->hw_handler_name) { - /* - * Increments scsi_dh reference, even when using an - * already-attached handler. - */ r = scsi_dh_attach(q, m->hw_handler_name); if (r == -EBUSY) { - /* - * Already attached to different hw_handler: - * try to reattach with correct one. - */ - scsi_dh_detach(q); - r = scsi_dh_attach(q, m->hw_handler_name); - } + char b[BDEVNAME_SIZE]; + printk(KERN_INFO "dm-mpath: retaining handler on device %s\n", + bdevname(p->path.dev->bdev, b)); + goto retain; + } if (r < 0) { ti->error = "error attaching hardware handler"; dm_put_device(ti, p->path.dev); @@ -624,7 +616,6 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps if (r < 0) { ti->error = "unable to set hardware " "handler parameters"; - scsi_dh_detach(q); dm_put_device(ti, p->path.dev); goto bad; } @@ -734,12 +725,6 @@ static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m) return 0; m->hw_handler_name = kstrdup(dm_shift_arg(as), GFP_KERNEL); - if (!try_then_request_module(scsi_dh_handler_exist(m->hw_handler_name), - "scsi_dh_%s", m->hw_handler_name)) { - ti->error = "unknown hardware handler type"; - ret = -EINVAL; - goto fail; - } if (hw_argc > 1) { char *p; diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 6578b7bc1fbb..6fcbfb063366 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -4249,6 +4249,10 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits) { struct thin_c *tc = ti->private; struct pool *pool = tc->pool; + struct queue_limits *pool_limits = dm_get_queue_limits(pool->pool_md); + + if (!pool_limits->discard_granularity) + return; /* pool's discard support is disabled */ limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT; limits->max_discard_sectors = 2048 * 1024 * 16; /* 16G */ diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index dc2aaab54aef..217d613b0fe7 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -10,6 +10,7 @@ config VIDEO_OMAP2_VOUT select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB + select FRAME_VECTOR default n ---help--- V4L2 Display driver support for OMAP2/3 based boards. diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index de2474e1132d..70c28d19ea04 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -195,46 +195,34 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix) } /* - * omap_vout_uservirt_to_phys: This inline function is used to convert user - * space virtual address to physical address. + * omap_vout_get_userptr: Convert user space virtual address to physical + * address. */ -static unsigned long omap_vout_uservirt_to_phys(unsigned long virtp) +static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp, + u32 *physp) { - unsigned long physp = 0; - struct vm_area_struct *vma; - struct mm_struct *mm = current->mm; + struct frame_vector *vec; + int ret; /* For kernel direct-mapped memory, take the easy way */ - if (virtp >= PAGE_OFFSET) - return virt_to_phys((void *) virtp); - - down_read(¤t->mm->mmap_sem); - vma = find_vma(mm, virtp); - if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { - /* this will catch, kernel-allocated, mmaped-to-usermode - addresses */ - physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); - up_read(¤t->mm->mmap_sem); - } else { - /* otherwise, use get_user_pages() for general userland pages */ - int res, nr_pages = 1; - struct page *pages; + if (virtp >= PAGE_OFFSET) { + *physp = virt_to_phys((void *)virtp); + return 0; + } - res = get_user_pages(current, current->mm, virtp, nr_pages, 1, - 0, &pages, NULL); - up_read(¤t->mm->mmap_sem); + vec = frame_vector_create(1); + if (!vec) + return -ENOMEM; - if (res == nr_pages) { - physp = __pa(page_address(&pages[0]) + - (virtp & ~PAGE_MASK)); - } else { - printk(KERN_WARNING VOUT_NAME - "get_user_pages failed\n"); - return 0; - } + ret = get_vaddr_frames(virtp, 1, true, false, vec); + if (ret != 1) { + frame_vector_destroy(vec); + return -EINVAL; } + *physp = __pfn_to_phys(frame_vector_pfns(vec)[0]); + vb->priv = vec; - return physp; + return 0; } /* @@ -784,11 +772,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q, * address of the buffer */ if (V4L2_MEMORY_USERPTR == vb->memory) { + int ret; + if (0 == vb->baddr) return -EINVAL; /* Physical address */ - vout->queued_buf_addr[vb->i] = (u8 *) - omap_vout_uservirt_to_phys(vb->baddr); + ret = omap_vout_get_userptr(vb, vb->baddr, + (u32 *)&vout->queued_buf_addr[vb->i]); + if (ret < 0) + return ret; } else { unsigned long addr, dma_addr; unsigned long size; @@ -834,12 +826,13 @@ static void omap_vout_buffer_queue(struct videobuf_queue *q, static void omap_vout_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { - struct omap_vout_device *vout = q->priv_data; - vb->state = VIDEOBUF_NEEDS_INIT; + if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) { + struct frame_vector *vec = vb->priv; - if (V4L2_MEMORY_MMAP != vout->memory) - return; + put_vaddr_frames(vec); + frame_vector_destroy(vec); + } } /* diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index b4b022933e29..82876a67f144 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -84,6 +84,7 @@ config VIDEOBUF2_CORE config VIDEOBUF2_MEMOPS tristate + select FRAME_VECTOR config VIDEOBUF2_DMA_CONTIG tristate diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index f1022d810d22..4f59b7ec05d0 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1691,9 +1691,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) ret = __qbuf_mmap(vb, b); break; case V4L2_MEMORY_USERPTR: - down_read(¤t->mm->mmap_sem); ret = __qbuf_userptr(vb, b); - up_read(¤t->mm->mmap_sem); break; case V4L2_MEMORY_DMABUF: ret = __qbuf_dmabuf(vb, b); diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 94c1e6455d36..2397ceb1dc6b 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -32,15 +32,13 @@ struct vb2_dc_buf { dma_addr_t dma_addr; enum dma_data_direction dma_dir; struct sg_table *dma_sgt; + struct frame_vector *vec; /* MMAP related */ struct vb2_vmarea_handler handler; atomic_t refcount; struct sg_table *sgt_base; - /* USERPTR related */ - struct vm_area_struct *vma; - /* DMABUF related */ struct dma_buf_attachment *db_attach; }; @@ -49,24 +47,6 @@ struct vb2_dc_buf { /* scatterlist table functions */ /*********************************************/ - -static void vb2_dc_sgt_foreach_page(struct sg_table *sgt, - void (*cb)(struct page *pg)) -{ - struct scatterlist *s; - unsigned int i; - - for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { - struct page *page = sg_page(s); - unsigned int n_pages = PAGE_ALIGN(s->offset + s->length) - >> PAGE_SHIFT; - unsigned int j; - - for (j = 0; j < n_pages; ++j, ++page) - cb(page); - } -} - static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt) { struct scatterlist *s; @@ -429,92 +409,12 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) /* callbacks for USERPTR buffers */ /*********************************************/ -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - -static int vb2_dc_get_user_pfn(unsigned long start, int n_pages, - struct vm_area_struct *vma, unsigned long *res) -{ - unsigned long pfn, start_pfn, prev_pfn; - unsigned int i; - int ret; - - if (!vma_is_io(vma)) - return -EFAULT; - - ret = follow_pfn(vma, start, &pfn); - if (ret) - return ret; - - start_pfn = pfn; - start += PAGE_SIZE; - - for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) { - prev_pfn = pfn; - ret = follow_pfn(vma, start, &pfn); - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - if (pfn != prev_pfn + 1) - return -EINVAL; - } - - *res = start_pfn; - return 0; -} - -static int vb2_dc_get_user_pages(unsigned long start, struct page **pages, - int n_pages, struct vm_area_struct *vma, - enum dma_data_direction dma_dir) -{ - if (vma_is_io(vma)) { - unsigned int i; - - for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) { - unsigned long pfn; - int ret = follow_pfn(vma, start, &pfn); - - if (!pfn_valid(pfn)) - return -EINVAL; - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - pages[i] = pfn_to_page(pfn); - } - } else { - int n; - - n = get_user_pages(current, current->mm, start & PAGE_MASK, - n_pages, dma_dir == DMA_FROM_DEVICE, 1, pages, NULL); - /* negative error means that no page was pinned */ - n = max(n, 0); - if (n != n_pages) { - pr_err("got only %d of %d user pages\n", n, n_pages); - while (n) - put_page(pages[--n]); - return -EFAULT; - } - } - - return 0; -} - -static void vb2_dc_put_dirty_page(struct page *page) -{ - set_page_dirty_lock(page); - put_page(page); -} - static void vb2_dc_put_userptr(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; + int i; + struct page **pages; if (sgt) { DEFINE_DMA_ATTRS(attrs); @@ -526,13 +426,15 @@ static void vb2_dc_put_userptr(void *buf_priv) */ dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir, &attrs); - if (!vma_is_io(buf->vma)) - vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page); - + pages = frame_vector_pages(buf->vec); + /* sgt should exist only if vector contains pages... */ + BUG_ON(IS_ERR(pages)); + for (i = 0; i < frame_vector_count(buf->vec); i++) + set_page_dirty_lock(pages[i]); sg_free_table(sgt); kfree(sgt); } - vb2_put_vma(buf->vma); + vb2_destroy_framevec(buf->vec); kfree(buf); } @@ -572,13 +474,10 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, { struct vb2_dc_conf *conf = alloc_ctx; struct vb2_dc_buf *buf; - unsigned long start; - unsigned long end; + struct frame_vector *vec; unsigned long offset; - struct page **pages; - int n_pages; + int n_pages, i; int ret = 0; - struct vm_area_struct *vma; struct sg_table *sgt; unsigned long contig_size; unsigned long dma_align = dma_get_cache_alignment(); @@ -604,72 +503,43 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->dev = conf->dev; buf->dma_dir = dma_dir; - start = vaddr & PAGE_MASK; offset = vaddr & ~PAGE_MASK; - end = PAGE_ALIGN(vaddr + size); - n_pages = (end - start) >> PAGE_SHIFT; - - pages = kmalloc(n_pages * sizeof(pages[0]), GFP_KERNEL); - if (!pages) { - ret = -ENOMEM; - pr_err("failed to allocate pages table\n"); + vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) { + ret = PTR_ERR(vec); goto fail_buf; } + buf->vec = vec; + n_pages = frame_vector_count(vec); + ret = frame_vector_to_pages(vec); + if (ret < 0) { + unsigned long *nums = frame_vector_pfns(vec); - /* current->mm->mmap_sem is taken by videobuf2 core */ - vma = find_vma(current->mm, vaddr); - if (!vma) { - pr_err("no vma for address %lu\n", vaddr); - ret = -EFAULT; - goto fail_pages; - } - - if (vma->vm_end < vaddr + size) { - pr_err("vma at %lu is too small for %lu bytes\n", vaddr, size); - ret = -EFAULT; - goto fail_pages; - } - - buf->vma = vb2_get_vma(vma); - if (!buf->vma) { - pr_err("failed to copy vma\n"); - ret = -ENOMEM; - goto fail_pages; - } - - /* extract page list from userspace mapping */ - ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, dma_dir); - if (ret) { - unsigned long pfn; - if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) { - buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, pfn); - buf->size = size; - kfree(pages); - return buf; - } - - pr_err("failed to get user pages\n"); - goto fail_vma; + /* + * Failed to convert to pages... Check the memory is physically + * contiguous and use direct mapping + */ + for (i = 1; i < n_pages; i++) + if (nums[i-1] + 1 != nums[i]) + goto fail_pfnvec; + buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, nums[0]); + goto out; } sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) { pr_err("failed to allocate sg table\n"); ret = -ENOMEM; - goto fail_get_user_pages; + goto fail_pfnvec; } - ret = sg_alloc_table_from_pages(sgt, pages, n_pages, + ret = sg_alloc_table_from_pages(sgt, frame_vector_pages(vec), n_pages, offset, size, GFP_KERNEL); if (ret) { pr_err("failed to initialize sg table\n"); goto fail_sgt; } - /* pages are no longer needed */ - kfree(pages); - pages = NULL; - /* * No need to sync to the device, this will happen later when the * prepare() memop is called. @@ -691,8 +561,9 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, } buf->dma_addr = sg_dma_address(sgt->sgl); - buf->size = size; buf->dma_sgt = sgt; +out: + buf->size = size; return buf; @@ -701,23 +572,13 @@ fail_map_sg: buf->dma_dir, &attrs); fail_sgt_init: - if (!vma_is_io(buf->vma)) - vb2_dc_sgt_foreach_page(sgt, put_page); sg_free_table(sgt); fail_sgt: kfree(sgt); -fail_get_user_pages: - if (pages && !vma_is_io(buf->vma)) - while (n_pages) - put_page(pages[--n_pages]); - -fail_vma: - vb2_put_vma(buf->vma); - -fail_pages: - kfree(pages); /* kfree is NULL-proof */ +fail_pfnvec: + vb2_destroy_framevec(vec); fail_buf: kfree(buf); diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index 7289b81bd7b7..be7bd6535c9d 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -38,6 +38,7 @@ struct vb2_dma_sg_buf { struct device *dev; void *vaddr; struct page **pages; + struct frame_vector *vec; int offset; enum dma_data_direction dma_dir; struct sg_table sg_table; @@ -51,7 +52,6 @@ struct vb2_dma_sg_buf { unsigned int num_pages; atomic_t refcount; struct vb2_vmarea_handler handler; - struct vm_area_struct *vma; struct dma_buf_attachment *db_attach; }; @@ -225,25 +225,17 @@ static void vb2_dma_sg_finish(void *buf_priv) dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); } -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, unsigned long size, enum dma_data_direction dma_dir) { struct vb2_dma_sg_conf *conf = alloc_ctx; struct vb2_dma_sg_buf *buf; - unsigned long first, last; - int num_pages_from_user; - struct vm_area_struct *vma; struct sg_table *sgt; DEFINE_DMA_ATTRS(attrs); + struct frame_vector *vec; dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); - buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return NULL; @@ -254,61 +246,19 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->offset = vaddr & ~PAGE_MASK; buf->size = size; buf->dma_sgt = &buf->sg_table; + vec = vb2_create_framevec(vaddr, size, buf->dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) + goto userptr_fail_pfnvec; + buf->vec = vec; - first = (vaddr & PAGE_MASK) >> PAGE_SHIFT; - last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT; - buf->num_pages = last - first + 1; - - buf->pages = kzalloc(buf->num_pages * sizeof(struct page *), - GFP_KERNEL); - if (!buf->pages) - goto userptr_fail_alloc_pages; - - vma = find_vma(current->mm, vaddr); - if (!vma) { - dprintk(1, "no vma for address %lu\n", vaddr); - goto userptr_fail_find_vma; - } - - if (vma->vm_end < vaddr + size) { - dprintk(1, "vma at %lu is too small for %lu bytes\n", - vaddr, size); - goto userptr_fail_find_vma; - } - - buf->vma = vb2_get_vma(vma); - if (!buf->vma) { - dprintk(1, "failed to copy vma\n"); - goto userptr_fail_find_vma; - } - - if (vma_is_io(buf->vma)) { - for (num_pages_from_user = 0; - num_pages_from_user < buf->num_pages; - ++num_pages_from_user, vaddr += PAGE_SIZE) { - unsigned long pfn; - - if (follow_pfn(vma, vaddr, &pfn)) { - dprintk(1, "no page for address %lu\n", vaddr); - break; - } - buf->pages[num_pages_from_user] = pfn_to_page(pfn); - } - } else - num_pages_from_user = get_user_pages(current, current->mm, - vaddr & PAGE_MASK, - buf->num_pages, - buf->dma_dir == DMA_FROM_DEVICE, - 1, /* force */ - buf->pages, - NULL); - - if (num_pages_from_user != buf->num_pages) - goto userptr_fail_get_user_pages; + buf->pages = frame_vector_pages(vec); + if (IS_ERR(buf->pages)) + goto userptr_fail_sgtable; + buf->num_pages = frame_vector_count(vec); if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages, buf->num_pages, buf->offset, size, 0)) - goto userptr_fail_alloc_table_from_pages; + goto userptr_fail_sgtable; sgt = &buf->sg_table; /* @@ -324,17 +274,9 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, userptr_fail_map: sg_free_table(&buf->sg_table); -userptr_fail_alloc_table_from_pages: -userptr_fail_get_user_pages: - dprintk(1, "get_user_pages requested/got: %d/%d]\n", - buf->num_pages, num_pages_from_user); - if (!vma_is_io(buf->vma)) - while (--num_pages_from_user >= 0) - put_page(buf->pages[num_pages_from_user]); - vb2_put_vma(buf->vma); -userptr_fail_find_vma: - kfree(buf->pages); -userptr_fail_alloc_pages: +userptr_fail_sgtable: + vb2_destroy_framevec(vec); +userptr_fail_pfnvec: kfree(buf); return NULL; } @@ -362,11 +304,8 @@ static void vb2_dma_sg_put_userptr(void *buf_priv) while (--i >= 0) { if (buf->dma_dir == DMA_FROM_DEVICE) set_page_dirty_lock(buf->pages[i]); - if (!vma_is_io(buf->vma)) - put_page(buf->pages[i]); } - kfree(buf->pages); - vb2_put_vma(buf->vma); + vb2_destroy_framevec(buf->vec); kfree(buf); } diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 0d49b7951f84..48c6a49c4928 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -23,118 +23,62 @@ #include <media/videobuf2-memops.h> /** - * vb2_get_vma() - acquire and lock the virtual memory area - * @vma: given virtual memory area + * vb2_create_framevec() - map virtual addresses to pfns + * @start: Virtual user address where we start mapping + * @length: Length of a range to map + * @write: Should we map for writing into the area * - * This function attempts to acquire an area mapped in the userspace for - * the duration of a hardware operation. The area is "locked" by performing - * the same set of operation that are done when process calls fork() and - * memory areas are duplicated. - * - * Returns a copy of a virtual memory region on success or NULL. - */ -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma) -{ - struct vm_area_struct *vma_copy; - - vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); - if (vma_copy == NULL) - return NULL; - - if (vma->vm_ops && vma->vm_ops->open) - vma->vm_ops->open(vma); - - if (vma->vm_file) - get_file(vma->vm_file); - - memcpy(vma_copy, vma, sizeof(*vma)); - - vma_copy->vm_mm = NULL; - vma_copy->vm_next = NULL; - vma_copy->vm_prev = NULL; - - return vma_copy; -} -EXPORT_SYMBOL_GPL(vb2_get_vma); - -/** - * vb2_put_userptr() - release a userspace virtual memory area - * @vma: virtual memory region associated with the area to be released - * - * This function releases the previously acquired memory area after a hardware - * operation. + * This function allocates and fills in a vector with pfns corresponding to + * virtual address range passed in arguments. If pfns have corresponding pages, + * page references are also grabbed to pin pages in memory. The function + * returns pointer to the vector on success and error pointer in case of + * failure. Returned vector needs to be freed via vb2_destroy_pfnvec(). */ -void vb2_put_vma(struct vm_area_struct *vma) +struct frame_vector *vb2_create_framevec(unsigned long start, + unsigned long length, + bool write) { - if (!vma) - return; - - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - - if (vma->vm_file) - fput(vma->vm_file); - - kfree(vma); + int ret; + unsigned long first, last; + unsigned long nr; + struct frame_vector *vec; + + first = start >> PAGE_SHIFT; + last = (start + length - 1) >> PAGE_SHIFT; + nr = last - first + 1; + vec = frame_vector_create(nr); + if (!vec) + return ERR_PTR(-ENOMEM); + ret = get_vaddr_frames(start, nr, write, 1, vec); + if (ret < 0) + goto out_destroy; + /* We accept only complete set of PFNs */ + if (ret != nr) { + ret = -EFAULT; + goto out_release; + } + return vec; +out_release: + put_vaddr_frames(vec); +out_destroy: + frame_vector_destroy(vec); + return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(vb2_put_vma); +EXPORT_SYMBOL(vb2_create_framevec); /** - * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory - * @vaddr: starting virtual address of the area to be verified - * @size: size of the area - * @res_paddr: will return physical address for the given vaddr - * @res_vma: will return locked copy of struct vm_area for the given area - * - * This function will go through memory area of size @size mapped at @vaddr and - * verify that the underlying physical pages are contiguous. If they are - * contiguous the virtual memory area is locked and a @res_vma is filled with - * the copy and @res_pa set to the physical address of the buffer. + * vb2_destroy_framevec() - release vector of mapped pfns + * @vec: vector of pfns / pages to release * - * Returns 0 on success. + * This releases references to all pages in the vector @vec (if corresponding + * pfns are backed by pages) and frees the passed vector. */ -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa) +void vb2_destroy_framevec(struct frame_vector *vec) { - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - unsigned long offset, start, end; - unsigned long this_pfn, prev_pfn; - dma_addr_t pa = 0; - - start = vaddr; - offset = start & ~PAGE_MASK; - end = start + size; - - vma = find_vma(mm, start); - - if (vma == NULL || vma->vm_end < end) - return -EFAULT; - - for (prev_pfn = 0; start < end; start += PAGE_SIZE) { - int ret = follow_pfn(vma, start, &this_pfn); - if (ret) - return ret; - - if (prev_pfn == 0) - pa = this_pfn << PAGE_SHIFT; - else if (this_pfn != prev_pfn + 1) - return -EFAULT; - - prev_pfn = this_pfn; - } - - /* - * Memory is contiguous, lock vma and return to the caller - */ - *res_vma = vb2_get_vma(vma); - if (*res_vma == NULL) - return -ENOMEM; - - *res_pa = pa + offset; - return 0; + put_vaddr_frames(vec); + frame_vector_destroy(vec); } -EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); +EXPORT_SYMBOL(vb2_destroy_framevec); /** * vb2_common_vm_open() - increase refcount of the vma diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c index 2fe4c27f524a..ecb8f0c7f025 100644 --- a/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -23,11 +23,9 @@ struct vb2_vmalloc_buf { void *vaddr; - struct page **pages; - struct vm_area_struct *vma; + struct frame_vector *vec; enum dma_data_direction dma_dir; unsigned long size; - unsigned int n_pages; atomic_t refcount; struct vb2_vmarea_handler handler; struct dma_buf *dbuf; @@ -76,10 +74,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, enum dma_data_direction dma_dir) { struct vb2_vmalloc_buf *buf; - unsigned long first, last; - int n_pages, offset; - struct vm_area_struct *vma; - dma_addr_t physp; + struct frame_vector *vec; + int n_pages, offset, i; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) @@ -88,51 +84,36 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->dma_dir = dma_dir; offset = vaddr & ~PAGE_MASK; buf->size = size; - - - vma = find_vma(current->mm, vaddr); - if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { - if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) - goto fail_pages_array_alloc; - buf->vma = vma; - buf->vaddr = (__force void *)ioremap_nocache(physp, size); - if (!buf->vaddr) - goto fail_pages_array_alloc; + vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) + goto fail_pfnvec_create; + buf->vec = vec; + n_pages = frame_vector_count(vec); + if (frame_vector_to_pages(vec) < 0) { + unsigned long *nums = frame_vector_pfns(vec); + + /* + * We cannot get page pointers for these pfns. Check memory is + * physically contiguous and use direct mapping. + */ + for (i = 1; i < n_pages; i++) + if (nums[i-1] + 1 != nums[i]) + goto fail_map; + buf->vaddr = (__force void *) + ioremap_nocache(nums[0] << PAGE_SHIFT, size); } else { - first = vaddr >> PAGE_SHIFT; - last = (vaddr + size - 1) >> PAGE_SHIFT; - buf->n_pages = last - first + 1; - buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), - GFP_KERNEL); - if (!buf->pages) - goto fail_pages_array_alloc; - - /* current->mm->mmap_sem is taken by videobuf2 core */ - n_pages = get_user_pages(current, current->mm, - vaddr & PAGE_MASK, buf->n_pages, - dma_dir == DMA_FROM_DEVICE, - 1, /* force */ - buf->pages, NULL); - if (n_pages != buf->n_pages) - goto fail_get_user_pages; - - buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, + buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1, PAGE_KERNEL); - if (!buf->vaddr) - goto fail_get_user_pages; } + if (!buf->vaddr) + goto fail_map; buf->vaddr += offset; return buf; -fail_get_user_pages: - pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages, - buf->n_pages); - while (--n_pages >= 0) - put_page(buf->pages[n_pages]); - kfree(buf->pages); - -fail_pages_array_alloc: +fail_map: + vb2_destroy_framevec(vec); +fail_pfnvec_create: kfree(buf); return NULL; @@ -143,20 +124,21 @@ static void vb2_vmalloc_put_userptr(void *buf_priv) struct vb2_vmalloc_buf *buf = buf_priv; unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK; unsigned int i; + struct page **pages; + unsigned int n_pages; - if (buf->pages) { + if (!buf->vec->is_pfns) { + n_pages = frame_vector_count(buf->vec); + pages = frame_vector_pages(buf->vec); if (vaddr) - vm_unmap_ram((void *)vaddr, buf->n_pages); - for (i = 0; i < buf->n_pages; ++i) { - if (buf->dma_dir == DMA_FROM_DEVICE) - set_page_dirty_lock(buf->pages[i]); - put_page(buf->pages[i]); - } - kfree(buf->pages); + vm_unmap_ram((void *)vaddr, n_pages); + if (buf->dma_dir == DMA_FROM_DEVICE) + for (i = 0; i < n_pages; i++) + set_page_dirty_lock(pages[i]); } else { - vb2_put_vma(buf->vma); iounmap((__force void __iomem *)buf->vaddr); } + vb2_destroy_framevec(buf->vec); kfree(buf); } diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 4b54128bc78e..a726f01e3b02 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -138,7 +138,7 @@ static void asic3_irq_flip_edge(struct asic3 *asic, spin_unlock_irqrestore(&asic->lock, flags); } -static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) +static void asic3_irq_demux(struct irq_desc *desc) { struct asic3 *asic = irq_desc_get_handler_data(desc); struct irq_data *data = irq_desc_get_irq_data(desc); diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c index a76eb6ef47a0..b279205659a4 100644 --- a/drivers/mfd/ezx-pcap.c +++ b/drivers/mfd/ezx-pcap.c @@ -205,7 +205,7 @@ static void pcap_isr_work(struct work_struct *work) } while (gpio_get_value(pdata->gpio)); } -static void pcap_irq_handler(unsigned int irq, struct irq_desc *desc) +static void pcap_irq_handler(struct irq_desc *desc) { struct pcap_chip *pcap = irq_desc_get_handler_data(desc); diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c index 9131cdcdc64a..6ccaf90d98fd 100644 --- a/drivers/mfd/htc-egpio.c +++ b/drivers/mfd/htc-egpio.c @@ -98,7 +98,7 @@ static struct irq_chip egpio_muxed_chip = { .irq_unmask = egpio_unmask, }; -static void egpio_handler(unsigned int irq, struct irq_desc *desc) +static void egpio_handler(struct irq_desc *desc) { struct egpio_info *ei = irq_desc_get_handler_data(desc); int irqpin; diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c index 5bb49f08955d..798e44306382 100644 --- a/drivers/mfd/jz4740-adc.c +++ b/drivers/mfd/jz4740-adc.c @@ -65,7 +65,7 @@ struct jz4740_adc { spinlock_t lock; }; -static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc) +static void jz4740_adc_irq_demux(struct irq_desc *desc) { struct irq_chip_generic *gc = irq_desc_get_handler_data(desc); uint8_t status; diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index 59502d02cd15..1b7ec0870c2a 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -156,7 +156,7 @@ static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master) return ret; } -static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc) +static void pm8xxx_irq_handler(struct irq_desc *desc) { struct pm_irq_chip *chip = irq_desc_get_handler_data(desc); struct irq_chip *irq_chip = irq_desc_get_chip(desc); diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c index 16fc1adc4fa3..94bd89cb1f06 100644 --- a/drivers/mfd/t7l66xb.c +++ b/drivers/mfd/t7l66xb.c @@ -185,7 +185,7 @@ static struct mfd_cell t7l66xb_cells[] = { /*--------------------------------------------------------------------------*/ /* Handle the T7L66XB interrupt mux */ -static void t7l66xb_irq(unsigned int irq, struct irq_desc *desc) +static void t7l66xb_irq(struct irq_desc *desc) { struct t7l66xb *t7l66xb = irq_desc_get_handler_data(desc); unsigned int isr; diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 775b9aca871a..8c84a513016b 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -522,8 +522,7 @@ static int tc6393xb_register_gpio(struct tc6393xb *tc6393xb, int gpio_base) /*--------------------------------------------------------------------------*/ -static void -tc6393xb_irq(unsigned int irq, struct irq_desc *desc) +static void tc6393xb_irq(struct irq_desc *desc) { struct tc6393xb *tc6393xb = irq_desc_get_handler_data(desc); unsigned int isr; diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 9a2302129711..f691d7ecad52 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -282,7 +282,7 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb) * SIBCLK to talk to the chip. We leave the clock running until * we have finished processing all interrupts from the chip. */ -static void ucb1x00_irq(unsigned int __irq, struct irq_desc *desc) +static void ucb1x00_irq(struct irq_desc *desc) { struct ucb1x00 *ucb = irq_desc_get_handler_data(desc); unsigned int isr, i; diff --git a/drivers/misc/cxl/Makefile b/drivers/misc/cxl/Makefile index 6f484dfe78f9..6982f603fadc 100644 --- a/drivers/misc/cxl/Makefile +++ b/drivers/misc/cxl/Makefile @@ -1,4 +1,4 @@ -ccflags-y := -Werror +ccflags-y := -Werror -Wno-unused-const-variable cxl-y += main.o file.o irq.o fault.o native.o cxl-y += context.o sysfs.o debugfs.o pci.o trace.o diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 02c85160bfe9..a5e977192b61 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -1249,8 +1249,6 @@ static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id) int slice; int rc; - pci_dev_get(dev); - if (cxl_verbose) dump_cxl_config_space(dev); diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index 25868c2ec03e..02006f7109a8 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c @@ -592,6 +592,8 @@ int cxl_sysfs_afu_add(struct cxl_afu *afu) /* conditionally create the add the binary file for error info buffer */ if (afu->eb_len) { + sysfs_attr_init(&afu->attr_eb.attr); + afu->attr_eb.attr.name = "afu_err_buff"; afu->attr_eb.attr.mode = S_IRUGO; afu->attr_eb.size = afu->eb_len; diff --git a/drivers/misc/cxl/vphb.c b/drivers/misc/cxl/vphb.c index 6dd16a6d153f..94b520896b18 100644 --- a/drivers/misc/cxl/vphb.c +++ b/drivers/misc/cxl/vphb.c @@ -48,6 +48,12 @@ static bool cxl_pci_enable_device_hook(struct pci_dev *dev) phb = pci_bus_to_host(dev->bus); afu = (struct cxl_afu *)phb->private_data; + + if (!cxl_adapter_link_ok(afu->adapter)) { + dev_warn(&dev->dev, "%s: Device link is down, refusing to enable AFU\n", __func__); + return false; + } + set_dma_ops(&dev->dev, &dma_direct_ops); set_dma_offset(&dev->dev, PAGE_OFFSET); diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index 4b469cf9e60f..8504dbeacd3b 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -204,6 +204,8 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name) if (!dir) return -ENOMEM; + dev->dbgfs_dir = dir; + f = debugfs_create_file("meclients", S_IRUSR, dir, dev, &mei_dbgfs_fops_meclients); if (!f) { @@ -228,7 +230,6 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name) dev_err(dev->dev, "allow_fixed_address: registration failed\n"); goto err; } - dev->dbgfs_dir = dir; return 0; err: mei_dbgfs_deregister(dev); diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index 2bc0f5089f82..b346638833b0 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c @@ -364,6 +364,7 @@ int mei_watchdog_register(struct mei_device *dev) int ret; + amt_wd_dev.parent = dev->dev; /* unlock to perserve correct locking order */ mutex_unlock(&dev->device_lock); ret = watchdog_register_device(&amt_wd_dev); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 0520064dc33b..a3eb20bdcd97 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -134,9 +134,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) int err = cmd->error; /* Flag re-tuning needed on CRC errors */ - if (err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) || + if ((cmd->opcode != MMC_SEND_TUNING_BLOCK && + cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) && + (err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) || (mrq->data && mrq->data->error == -EILSEQ) || - (mrq->stop && mrq->stop->error == -EILSEQ)) + (mrq->stop && mrq->stop->error == -EILSEQ))) mmc_retune_needed(host); if (err && cmd->retries && mmc_host_is_spi(host)) { diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index abd933b7029b..5466f25f0281 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -457,7 +457,7 @@ int mmc_of_parse(struct mmc_host *host) 0, &cd_gpio_invert); if (!ret) dev_info(host->parent, "Got CD GPIO\n"); - else if (ret != -ENOENT) + else if (ret != -ENOENT && ret != -ENOSYS) return ret; /* @@ -481,7 +481,7 @@ int mmc_of_parse(struct mmc_host *host) ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert); if (!ret) dev_info(host->parent, "Got WP GPIO\n"); - else if (ret != -ENOENT) + else if (ret != -ENOENT && ret != -ENOSYS) return ret; if (of_property_read_bool(np, "disable-wp")) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 1420f29628c7..8cadd74e8407 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -28,6 +28,7 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/mmc/host.h> +#include <linux/mmc/slot-gpio.h> #include <linux/io.h> #include <linux/regulator/consumer.h> #include <linux/gpio.h> @@ -454,12 +455,8 @@ static int pxamci_get_ro(struct mmc_host *mmc) { struct pxamci_host *host = mmc_priv(mmc); - if (host->pdata && gpio_is_valid(host->pdata->gpio_card_ro)) { - if (host->pdata->gpio_card_ro_invert) - return !gpio_get_value(host->pdata->gpio_card_ro); - else - return gpio_get_value(host->pdata->gpio_card_ro); - } + if (host->pdata && gpio_is_valid(host->pdata->gpio_card_ro)) + return mmc_gpio_get_ro(mmc); if (host->pdata && host->pdata->get_ro) return !!host->pdata->get_ro(mmc_dev(mmc)); /* @@ -551,6 +548,7 @@ static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable) static const struct mmc_host_ops pxamci_ops = { .request = pxamci_request, + .get_cd = mmc_gpio_get_cd, .get_ro = pxamci_get_ro, .set_ios = pxamci_set_ios, .enable_sdio_irq = pxamci_enable_sdio_irq, @@ -790,37 +788,31 @@ static int pxamci_probe(struct platform_device *pdev) gpio_power = host->pdata->gpio_power; } if (gpio_is_valid(gpio_power)) { - ret = gpio_request(gpio_power, "mmc card power"); + ret = devm_gpio_request(&pdev->dev, gpio_power, + "mmc card power"); if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_power %d\n", gpio_power); + dev_err(&pdev->dev, "Failed requesting gpio_power %d\n", + gpio_power); goto out; } gpio_direction_output(gpio_power, host->pdata->gpio_power_invert); } - if (gpio_is_valid(gpio_ro)) { - ret = gpio_request(gpio_ro, "mmc card read only"); - if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_ro); - goto err_gpio_ro; - } - gpio_direction_input(gpio_ro); + if (gpio_is_valid(gpio_ro)) + ret = mmc_gpio_request_ro(mmc, gpio_ro); + if (ret) { + dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_ro); + goto out; + } else { + mmc->caps |= host->pdata->gpio_card_ro_invert ? + MMC_CAP2_RO_ACTIVE_HIGH : 0; } - if (gpio_is_valid(gpio_cd)) { - ret = gpio_request(gpio_cd, "mmc card detect"); - if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", gpio_cd); - goto err_gpio_cd; - } - gpio_direction_input(gpio_cd); - ret = request_irq(gpio_to_irq(gpio_cd), pxamci_detect_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "mmc card detect", mmc); - if (ret) { - dev_err(&pdev->dev, "failed to request card detect IRQ\n"); - goto err_request_irq; - } + if (gpio_is_valid(gpio_cd)) + ret = mmc_gpio_request_cd(mmc, gpio_cd, 0); + if (ret) { + dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", gpio_cd); + goto out; } if (host->pdata && host->pdata->init) @@ -835,13 +827,7 @@ static int pxamci_probe(struct platform_device *pdev) return 0; -err_request_irq: - gpio_free(gpio_cd); -err_gpio_cd: - gpio_free(gpio_ro); -err_gpio_ro: - gpio_free(gpio_power); - out: +out: if (host) { if (host->dma_chan_rx) dma_release_channel(host->dma_chan_rx); @@ -873,14 +859,6 @@ static int pxamci_remove(struct platform_device *pdev) gpio_ro = host->pdata->gpio_card_ro; gpio_power = host->pdata->gpio_power; } - if (gpio_is_valid(gpio_cd)) { - free_irq(gpio_to_irq(gpio_cd), mmc); - gpio_free(gpio_cd); - } - if (gpio_is_valid(gpio_ro)) - gpio_free(gpio_ro); - if (gpio_is_valid(gpio_power)) - gpio_free(gpio_power); if (host->vcc) regulator_put(host->vcc); diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index a7b7a6771598..b981b8552e43 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -210,6 +210,16 @@ #define SDXC_IDMAC_DES0_CES BIT(30) /* card error summary */ #define SDXC_IDMAC_DES0_OWN BIT(31) /* 1-idma owns it, 0-host owns it */ +#define SDXC_CLK_400K 0 +#define SDXC_CLK_25M 1 +#define SDXC_CLK_50M 2 +#define SDXC_CLK_50M_DDR 3 + +struct sunxi_mmc_clk_delay { + u32 output; + u32 sample; +}; + struct sunxi_idma_des { u32 config; u32 buf_size; @@ -229,6 +239,7 @@ struct sunxi_mmc_host { struct clk *clk_mmc; struct clk *clk_sample; struct clk *clk_output; + const struct sunxi_mmc_clk_delay *clk_delays; /* irq */ spinlock_t lock; @@ -654,25 +665,19 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, /* determine delays */ if (rate <= 400000) { - oclk_dly = 180; - sclk_dly = 42; + oclk_dly = host->clk_delays[SDXC_CLK_400K].output; + sclk_dly = host->clk_delays[SDXC_CLK_400K].sample; } else if (rate <= 25000000) { - oclk_dly = 180; - sclk_dly = 75; + oclk_dly = host->clk_delays[SDXC_CLK_25M].output; + sclk_dly = host->clk_delays[SDXC_CLK_25M].sample; } else if (rate <= 50000000) { if (ios->timing == MMC_TIMING_UHS_DDR50) { - oclk_dly = 60; - sclk_dly = 120; + oclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].output; + sclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].sample; } else { - oclk_dly = 90; - sclk_dly = 150; + oclk_dly = host->clk_delays[SDXC_CLK_50M].output; + sclk_dly = host->clk_delays[SDXC_CLK_50M].sample; } - } else if (rate <= 100000000) { - oclk_dly = 6; - sclk_dly = 24; - } else if (rate <= 200000000) { - oclk_dly = 3; - sclk_dly = 12; } else { return -EINVAL; } @@ -871,6 +876,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) static const struct of_device_id sunxi_mmc_of_match[] = { { .compatible = "allwinner,sun4i-a10-mmc", }, { .compatible = "allwinner,sun5i-a13-mmc", }, + { .compatible = "allwinner,sun9i-a80-mmc", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); @@ -884,6 +890,20 @@ static struct mmc_host_ops sunxi_mmc_ops = { .hw_reset = sunxi_mmc_hw_reset, }; +static const struct sunxi_mmc_clk_delay sunxi_mmc_clk_delays[] = { + [SDXC_CLK_400K] = { .output = 180, .sample = 180 }, + [SDXC_CLK_25M] = { .output = 180, .sample = 75 }, + [SDXC_CLK_50M] = { .output = 90, .sample = 120 }, + [SDXC_CLK_50M_DDR] = { .output = 60, .sample = 120 }, +}; + +static const struct sunxi_mmc_clk_delay sun9i_mmc_clk_delays[] = { + [SDXC_CLK_400K] = { .output = 180, .sample = 180 }, + [SDXC_CLK_25M] = { .output = 180, .sample = 75 }, + [SDXC_CLK_50M] = { .output = 150, .sample = 120 }, + [SDXC_CLK_50M_DDR] = { .output = 90, .sample = 120 }, +}; + static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, struct platform_device *pdev) { @@ -895,6 +915,11 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, else host->idma_des_size_bits = 16; + if (of_device_is_compatible(np, "allwinner,sun9i-a80-mmc")) + host->clk_delays = sun9i_mmc_clk_delays; + else + host->clk_delays = sunxi_mmc_clk_delays; + ret = mmc_regulator_get_supply(host->mmc); if (ret) { if (ret != -EPROBE_DEFER) diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 5bbd1f094f4e..1fc23e48fe8e 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -926,6 +926,11 @@ static int validate_vid_hdr(const struct ubi_device *ubi, goto bad; } + if (data_size > ubi->leb_size) { + ubi_err(ubi, "bad data_size"); + goto bad; + } + if (vol_type == UBI_VID_STATIC) { /* * Although from high-level point of view static volumes may diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index 80bdd5b88bac..d85c19762160 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -649,6 +649,7 @@ static int init_volumes(struct ubi_device *ubi, if (ubi->corr_peb_count) ubi_err(ubi, "%d PEBs are corrupted and not used", ubi->corr_peb_count); + return -ENOSPC; } ubi->rsvd_pebs += reserved_pebs; ubi->avail_pebs -= reserved_pebs; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 275d9fb6fe5c..eb4489f9082f 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -1601,6 +1601,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) if (ubi->corr_peb_count) ubi_err(ubi, "%d PEBs are corrupted and not used", ubi->corr_peb_count); + err = -ENOSPC; goto out_free; } ubi->avail_pebs -= reserved_pebs; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index d18eb607bee6..b9ebd0d18a52 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -299,6 +299,7 @@ config NLMON config NET_VRF tristate "Virtual Routing and Forwarding (Lite)" depends on IP_MULTIPLE_TABLES && IPV6_MULTIPLE_TABLES + depends on NET_L3_MASTER_DEV ---help--- This option enables the support for mapping interfaces into VRF's. The support enables VRF devices. diff --git a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c index 705e6ce2eb90..d78f30186642 100644 --- a/drivers/net/arcnet/arc-rawmode.c +++ b/drivers/net/arcnet/arc-rawmode.c @@ -1,6 +1,6 @@ /* * Linux ARCnet driver - "raw mode" packet encapsulation (no soft headers) - * + * * Written 1994-1999 by Avery Pennarun. * Derived from skeleton.c by Donald Becker. * @@ -24,6 +24,8 @@ * ********************** */ +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/gfp.h> #include <linux/init.h> @@ -31,58 +33,7 @@ #include <net/arp.h> #include <linux/netdevice.h> #include <linux/skbuff.h> -#include <linux/arcdevice.h> - -#define VERSION "arcnet: raw mode (`r') encapsulation support loaded.\n" - - -static void rx(struct net_device *dev, int bufnum, - struct archdr *pkthdr, int length); -static int build_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, uint8_t daddr); -static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, - int bufnum); - -static struct ArcProto rawmode_proto = -{ - .suffix = 'r', - .mtu = XMTU, - .rx = rx, - .build_header = build_header, - .prepare_tx = prepare_tx, - .continue_tx = NULL, - .ack_tx = NULL -}; - - -static int __init arcnet_raw_init(void) -{ - int count; - - printk(VERSION); - - for (count = 0; count < 256; count++) - if (arc_proto_map[count] == arc_proto_default) - arc_proto_map[count] = &rawmode_proto; - - /* for raw mode, we only set the bcast proto if there's no better one */ - if (arc_bcast_proto == arc_proto_default) - arc_bcast_proto = &rawmode_proto; - - arc_proto_default = &rawmode_proto; - return 0; -} - -static void __exit arcnet_raw_exit(void) -{ - arcnet_unregister_proto(&rawmode_proto); -} - -module_init(arcnet_raw_init); -module_exit(arcnet_raw_exit); - -MODULE_LICENSE("GPL"); - +#include "arcdevice.h" /* packet receiver */ static void rx(struct net_device *dev, int bufnum, @@ -93,7 +44,7 @@ static void rx(struct net_device *dev, int bufnum, struct archdr *pkt = pkthdr; int ofs; - BUGMSG(D_DURING, "it's a raw packet (length=%d)\n", length); + arc_printk(D_DURING, dev, "it's a raw packet (length=%d)\n", length); if (length > MTU) ofs = 512 - length; @@ -101,15 +52,14 @@ static void rx(struct net_device *dev, int bufnum, ofs = 256 - length; skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC); - if (skb == NULL) { - BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n"); + if (!skb) { dev->stats.rx_dropped++; return; } skb_put(skb, length + ARC_HDR_SIZE); skb->dev = dev; - pkt = (struct archdr *) skb->data; + pkt = (struct archdr *)skb->data; skb_reset_mac_header(skb); skb_pull(skb, ARC_HDR_SIZE); @@ -121,38 +71,35 @@ static void rx(struct net_device *dev, int bufnum, pkt->soft.raw + sizeof(pkt->soft), length - sizeof(pkt->soft)); - BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx"); + if (BUGLVL(D_SKB)) + arcnet_dump_skb(dev, skb, "rx"); skb->protocol = cpu_to_be16(ETH_P_ARCNET); netif_rx(skb); } - -/* - * Create the ARCnet hard/soft headers for raw mode. +/* Create the ARCnet hard/soft headers for raw mode. * There aren't any soft headers in raw mode - not even the protocol id. */ static int build_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, uint8_t daddr) { int hdr_size = ARC_HDR_SIZE; - struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size); + struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size); - /* - * Set the source hardware address. + /* Set the source hardware address. * * This is pretty pointless for most purposes, but it can help in - * debugging. ARCnet does not allow us to change the source address in - * the actual packet sent) + * debugging. ARCnet does not allow us to change the source address + * in the actual packet sent. */ pkt->hard.source = *dev->dev_addr; /* see linux/net/ethernet/eth.c to see where I got the following */ if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { - /* - * FIXME: fill in the last byte of the dest ipaddr here to better - * comply with RFC1051 in "noarp" mode. + /* FIXME: fill in the last byte of the dest ipaddr here + * to better comply with RFC1051 in "noarp" mode. */ pkt->hard.dest = 0; return hdr_size; @@ -163,7 +110,6 @@ static int build_header(struct sk_buff *skb, struct net_device *dev, return hdr_size; /* success */ } - static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, int bufnum) { @@ -171,15 +117,16 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, struct arc_hardware *hard = &pkt->hard; int ofs; - BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n", - lp->next_tx, lp->cur_tx, bufnum); + arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n", + lp->next_tx, lp->cur_tx, bufnum); - length -= ARC_HDR_SIZE; /* hard header is not included in packet length */ + /* hard header is not included in packet length */ + length -= ARC_HDR_SIZE; if (length > XMTU) { /* should never happen! other people already check for this. */ - BUGMSG(D_NORMAL, "Bug! prepare_tx with size %d (> %d)\n", - length, XMTU); + arc_printk(D_NORMAL, dev, "Bug! prepare_tx with size %d (> %d)\n", + length, XMTU); length = XMTU; } if (length >= MinTU) { @@ -188,11 +135,12 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, } else if (length > MTU) { hard->offset[0] = 0; hard->offset[1] = ofs = 512 - length - 3; - } else + } else { hard->offset[0] = ofs = 256 - length; + } - BUGMSG(D_DURING, "prepare_tx: length=%d ofs=%d\n", - length,ofs); + arc_printk(D_DURING, dev, "prepare_tx: length=%d ofs=%d\n", + length, ofs); lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE); lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft, length); @@ -201,3 +149,41 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, return 1; /* done */ } + +static struct ArcProto rawmode_proto = { + .suffix = 'r', + .mtu = XMTU, + .rx = rx, + .build_header = build_header, + .prepare_tx = prepare_tx, + .continue_tx = NULL, + .ack_tx = NULL +}; + +static int __init arcnet_raw_init(void) +{ + int count; + + pr_info("raw mode (`r') encapsulation support loaded\n"); + + for (count = 0; count < 256; count++) + if (arc_proto_map[count] == arc_proto_default) + arc_proto_map[count] = &rawmode_proto; + + /* for raw mode, we only set the bcast proto if there's no better one */ + if (arc_bcast_proto == arc_proto_default) + arc_bcast_proto = &rawmode_proto; + + arc_proto_default = &rawmode_proto; + return 0; +} + +static void __exit arcnet_raw_exit(void) +{ + arcnet_unregister_proto(&rawmode_proto); +} + +module_init(arcnet_raw_init); +module_exit(arcnet_raw_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c index b8b4c7ba884f..a07e24970be4 100644 --- a/drivers/net/arcnet/arc-rimi.c +++ b/drivers/net/arcnet/arc-rimi.c @@ -1,6 +1,6 @@ /* * Linux ARCnet driver - "RIM I" (entirely mem-mapped) cards - * + * * Written 1994-1999 by Avery Pennarun. * Written 1999-2000 by Martin Mares <mj@ucw.cz>. * Derived from skeleton.c by Donald Becker. @@ -24,6 +24,9 @@ * * ********************** */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -33,12 +36,10 @@ #include <linux/bootmem.h> #include <linux/init.h> #include <linux/interrupt.h> -#include <asm/io.h> -#include <linux/arcdevice.h> - - -#define VERSION "arcnet: RIM I (entirely mem-mapped) support\n" +#include <linux/io.h> +#include "arcdevice.h" +#include "com9026.h" /* Internal function declarations */ @@ -50,66 +51,46 @@ static void arcrimi_setmask(struct net_device *dev, int mask); static int arcrimi_reset(struct net_device *dev, int really_reset); static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset, void *buf, int count); -static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count); +static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, + int offset, void *buf, int count); /* Handy defines for ARCnet specific stuff */ /* Amount of I/O memory used by the card */ -#define BUFFER_SIZE (512) -#define MIRROR_SIZE (BUFFER_SIZE*4) +#define BUFFER_SIZE (512) +#define MIRROR_SIZE (BUFFER_SIZE * 4) -/* COM 9026 controller chip --> ARCnet register addresses */ -#define _INTMASK (ioaddr+0) /* writable */ -#define _STATUS (ioaddr+0) /* readable */ -#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */ -#define _RESET (ioaddr+8) /* software reset (on read) */ -#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */ -#define _ADDR_HI (ioaddr+15) /* Control registers for said */ -#define _ADDR_LO (ioaddr+14) -#define _CONFIG (ioaddr+2) /* Configuration register */ - -#undef ASTATUS -#undef ACOMMAND -#undef AINTMASK - -#define ASTATUS() readb(_STATUS) -#define ACOMMAND(cmd) writeb((cmd),_COMMAND) -#define AINTMASK(msk) writeb((msk),_INTMASK) -#define SETCONF() writeb(lp->config,_CONFIG) - - -/* - * We cannot probe for a RIM I card; one reason is I don't know how to reset +/* We cannot probe for a RIM I card; one reason is I don't know how to reset * them. In fact, we can't even get their node ID automatically. So, we * need to be passed a specific shmem address, IRQ, and node ID. */ static int __init arcrimi_probe(struct net_device *dev) { - BUGLVL(D_NORMAL) printk(VERSION); - BUGLVL(D_NORMAL) printk("E-mail me if you actually test the RIM I driver, please!\n"); - - BUGLVL(D_NORMAL) printk("Given: node %02Xh, shmem %lXh, irq %d\n", - dev->dev_addr[0], dev->mem_start, dev->irq); + if (BUGLVL(D_NORMAL)) { + pr_info("%s\n", "RIM I (entirely mem-mapped) support"); + pr_info("E-mail me if you actually test the RIM I driver, please!\n"); + pr_info("Given: node %02Xh, shmem %lXh, irq %d\n", + dev->dev_addr[0], dev->mem_start, dev->irq); + } if (dev->mem_start <= 0 || dev->irq <= 0) { - BUGLVL(D_NORMAL) printk("No autoprobe for RIM I; you " - "must specify the shmem and irq!\n"); + if (BUGLVL(D_NORMAL)) + pr_err("No autoprobe for RIM I; you must specify the shmem and irq!\n"); return -ENODEV; } if (dev->dev_addr[0] == 0) { - BUGLVL(D_NORMAL) printk("You need to specify your card's station " - "ID!\n"); + if (BUGLVL(D_NORMAL)) + pr_err("You need to specify your card's station ID!\n"); return -ENODEV; } - /* - * Grab the memory region at mem_start for MIRROR_SIZE bytes. + /* Grab the memory region at mem_start for MIRROR_SIZE bytes. * Later in arcrimi_found() the real size will be determined * and this reserve will be released and the correct size * will be taken. */ if (!request_mem_region(dev->mem_start, MIRROR_SIZE, "arcnet (90xx)")) { - BUGLVL(D_NORMAL) printk("Card memory already allocated\n"); + if (BUGLVL(D_NORMAL)) + pr_notice("Card memory already allocated\n"); return -ENODEV; } return arcrimi_found(dev); @@ -125,7 +106,7 @@ static int check_mirror(unsigned long addr, size_t size) p = ioremap(addr, size); if (p) { - if (readb(p) == TESTvalue) + if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue) res = 1; else res = 0; @@ -136,9 +117,8 @@ static int check_mirror(unsigned long addr, size_t size) return res; } -/* - * Set up the struct net_device associated with this card. Called after - * probing succeeds. +/* Set up the struct net_device associated with this card. + * Called after probing succeeds. */ static int __init arcrimi_found(struct net_device *dev) { @@ -151,7 +131,7 @@ static int __init arcrimi_found(struct net_device *dev) p = ioremap(dev->mem_start, MIRROR_SIZE); if (!p) { release_mem_region(dev->mem_start, MIRROR_SIZE); - BUGMSG(D_NORMAL, "Can't ioremap\n"); + arc_printk(D_NORMAL, dev, "Can't ioremap\n"); return -ENODEV; } @@ -159,13 +139,14 @@ static int __init arcrimi_found(struct net_device *dev) if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (RIM I)", dev)) { iounmap(p); release_mem_region(dev->mem_start, MIRROR_SIZE); - BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); + arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq); return -ENODEV; } shmem = dev->mem_start; - writeb(TESTvalue, p); - writeb(dev->dev_addr[0], p + 1); /* actually the node ID */ + arcnet_writeb(TESTvalue, p, COM9026_REG_W_INTMASK); + arcnet_writeb(TESTvalue, p, COM9026_REG_W_COMMAND); + /* actually the station/node ID */ /* find the real shared memory start/end points, including mirrors */ @@ -174,7 +155,7 @@ static int __init arcrimi_found(struct net_device *dev) * 2k (or there are no mirrors at all) but on some, it's 4k. */ mirror_size = MIRROR_SIZE; - if (readb(p) == TESTvalue && + if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue && check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 && check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1) mirror_size = 2 * MIRROR_SIZE; @@ -204,8 +185,7 @@ static int __init arcrimi_found(struct net_device *dev) lp->hw.copy_to_card = arcrimi_copy_to_card; lp->hw.copy_from_card = arcrimi_copy_from_card; - /* - * re-reserve the memory region - arcrimi_probe() alloced this reqion + /* re-reserve the memory region - arcrimi_probe() alloced this reqion * but didn't know the real size. Free that region and then re-get * with the correct size. There is a VERY slim chance this could * fail. @@ -215,24 +195,25 @@ static int __init arcrimi_found(struct net_device *dev) if (!request_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1, "arcnet (90xx)")) { - BUGMSG(D_NORMAL, "Card memory already allocated\n"); + arc_printk(D_NORMAL, dev, "Card memory already allocated\n"); goto err_free_irq; } - lp->mem_start = ioremap(dev->mem_start, dev->mem_end - dev->mem_start + 1); + lp->mem_start = ioremap(dev->mem_start, + dev->mem_end - dev->mem_start + 1); if (!lp->mem_start) { - BUGMSG(D_NORMAL, "Can't remap device memory!\n"); + arc_printk(D_NORMAL, dev, "Can't remap device memory!\n"); goto err_release_mem; } /* get and check the station ID from offset 1 in shmem */ - dev->dev_addr[0] = readb(lp->mem_start + 1); + dev->dev_addr[0] = arcnet_readb(lp->mem_start, COM9026_REG_R_STATION); - BUGMSG(D_NORMAL, "ARCnet RIM I: station %02Xh found at IRQ %d, " - "ShMem %lXh (%ld*%d bytes).\n", - dev->dev_addr[0], - dev->irq, dev->mem_start, - (dev->mem_end - dev->mem_start + 1) / mirror_size, mirror_size); + arc_printk(D_NORMAL, dev, "ARCnet RIM I: station %02Xh found at IRQ %d, ShMem %lXh (%ld*%d bytes)\n", + dev->dev_addr[0], + dev->irq, dev->mem_start, + (dev->mem_end - dev->mem_start + 1) / mirror_size, + mirror_size); err = register_netdev(dev); if (err) @@ -249,9 +230,7 @@ err_free_irq: return -EIO; } - -/* - * Do a hardware reset on the card, and set up necessary registers. +/* Do a hardware reset on the card, and set up necessary registers. * * This should be called as little as possible, because it disrupts the * token on the network (causes a RECON) and requires a significant delay. @@ -263,17 +242,19 @@ static int arcrimi_reset(struct net_device *dev, int really_reset) struct arcnet_local *lp = netdev_priv(dev); void __iomem *ioaddr = lp->mem_start + 0x800; - BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", dev->name, ASTATUS()); + arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n", + dev->name, arcnet_readb(ioaddr, COM9026_REG_R_STATUS)); if (really_reset) { - writeb(TESTvalue, ioaddr - 0x800); /* fake reset */ + arcnet_writeb(TESTvalue, ioaddr, -0x800); /* fake reset */ return 0; } - ACOMMAND(CFLAGScmd | RESETclear); /* clear flags & end reset */ - ACOMMAND(CFLAGScmd | CONFIGclear); + /* clear flags & end reset */ + arcnet_writeb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND); + arcnet_writeb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND); /* enable extended (512-byte) packets */ - ACOMMAND(CONFIGcmd | EXTconf); + arcnet_writeb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND); /* done! return success. */ return 0; @@ -284,7 +265,7 @@ static void arcrimi_setmask(struct net_device *dev, int mask) struct arcnet_local *lp = netdev_priv(dev); void __iomem *ioaddr = lp->mem_start + 0x800; - AINTMASK(mask); + arcnet_writeb(mask, ioaddr, COM9026_REG_W_INTMASK); } static int arcrimi_status(struct net_device *dev) @@ -292,7 +273,7 @@ static int arcrimi_status(struct net_device *dev) struct arcnet_local *lp = netdev_priv(dev); void __iomem *ioaddr = lp->mem_start + 0x800; - return ASTATUS(); + return arcnet_readb(ioaddr, COM9026_REG_R_STATUS); } static void arcrimi_command(struct net_device *dev, int cmd) @@ -300,7 +281,7 @@ static void arcrimi_command(struct net_device *dev, int cmd) struct arcnet_local *lp = netdev_priv(dev); void __iomem *ioaddr = lp->mem_start + 0x800; - ACOMMAND(cmd); + arcnet_writeb(cmd, ioaddr, COM9026_REG_W_COMMAND); } static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset, @@ -308,16 +289,17 @@ static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset, { struct arcnet_local *lp = netdev_priv(dev); void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset; - TIME("memcpy_toio", count, memcpy_toio(memaddr, buf, count)); -} + TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count)); +} -static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count) +static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, + int offset, void *buf, int count) { struct arcnet_local *lp = netdev_priv(dev); void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset; - TIME("memcpy_fromio", count, memcpy_fromio(buf, memaddr, count)); + + TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count)); } static int node; @@ -374,12 +356,13 @@ static void __exit arc_rimi_exit(void) static int __init arcrimi_setup(char *s) { int ints[8]; + s = get_options(s, 8, ints); if (!ints[0]) return 1; switch (ints[0]) { default: /* ERROR */ - printk("arcrimi: Too many arguments.\n"); + pr_err("Too many arguments\n"); case 3: /* Node ID */ node = ints[3]; case 2: /* IRQ */ diff --git a/drivers/net/arcnet/arcdevice.h b/drivers/net/arcnet/arcdevice.h new file mode 100644 index 000000000000..d7fdea11e694 --- /dev/null +++ b/drivers/net/arcnet/arcdevice.h @@ -0,0 +1,368 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. NET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions used by the ARCnet driver. + * + * Authors: Avery Pennarun and David Woodhouse + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ +#ifndef _LINUX_ARCDEVICE_H +#define _LINUX_ARCDEVICE_H + +#include <asm/timex.h> +#include <linux/if_arcnet.h> + +#ifdef __KERNEL__ +#include <linux/irqreturn.h> + +/* + * RECON_THRESHOLD is the maximum number of RECON messages to receive + * within one minute before printing a "cabling problem" warning. The + * default value should be fine. + * + * After that, a "cabling restored" message will be printed on the next IRQ + * if no RECON messages have been received for 10 seconds. + * + * Do not define RECON_THRESHOLD at all if you want to disable this feature. + */ +#define RECON_THRESHOLD 30 + +/* + * Define this to the minimum "timeout" value. If a transmit takes longer + * than TX_TIMEOUT jiffies, Linux will abort the TX and retry. On a large + * network, or one with heavy network traffic, this timeout may need to be + * increased. The larger it is, though, the longer it will be between + * necessary transmits - don't set this too high. + */ +#define TX_TIMEOUT (HZ * 200 / 1000) + +/* Display warnings about the driver being an ALPHA version. */ +#undef ALPHA_WARNING + +/* + * Debugging bitflags: each option can be enabled individually. + * + * Note: only debug flags included in the ARCNET_DEBUG_MAX define will + * actually be available. GCC will (at least, GCC 2.7.0 will) notice + * lines using a BUGLVL not in ARCNET_DEBUG_MAX and automatically optimize + * them out. + */ +#define D_NORMAL 1 /* important operational info */ +#define D_EXTRA 2 /* useful, but non-vital information */ +#define D_INIT 4 /* show init/probe messages */ +#define D_INIT_REASONS 8 /* show reasons for discarding probes */ +#define D_RECON 32 /* print a message whenever token is lost */ +#define D_PROTO 64 /* debug auto-protocol support */ +/* debug levels below give LOTS of output during normal operation! */ +#define D_DURING 128 /* trace operations (including irq's) */ +#define D_TX 256 /* show tx packets */ +#define D_RX 512 /* show rx packets */ +#define D_SKB 1024 /* show skb's */ +#define D_SKB_SIZE 2048 /* show skb sizes */ +#define D_TIMING 4096 /* show time needed to copy buffers to card */ +#define D_DEBUG 8192 /* Very detailed debug line for line */ + +#ifndef ARCNET_DEBUG_MAX +#define ARCNET_DEBUG_MAX (127) /* change to ~0 if you want detailed debugging */ +#endif + +#ifndef ARCNET_DEBUG +#define ARCNET_DEBUG (D_NORMAL | D_EXTRA) +#endif +extern int arcnet_debug; + +#define BUGLVL(x) ((x) & ARCNET_DEBUG_MAX & arcnet_debug) + +/* macros to simplify debug checking */ +#define arc_printk(x, dev, fmt, ...) \ +do { \ + if (BUGLVL(x)) { \ + if ((x) == D_NORMAL) \ + netdev_warn(dev, fmt, ##__VA_ARGS__); \ + else if ((x) < D_DURING) \ + netdev_info(dev, fmt, ##__VA_ARGS__); \ + else \ + netdev_dbg(dev, fmt, ##__VA_ARGS__); \ + } \ +} while (0) + +#define arc_cont(x, fmt, ...) \ +do { \ + if (BUGLVL(x)) \ + pr_cont(fmt, ##__VA_ARGS__); \ +} while (0) + +/* see how long a function call takes to run, expressed in CPU cycles */ +#define TIME(dev, name, bytes, call) \ +do { \ + if (BUGLVL(D_TIMING)) { \ + unsigned long _x, _y; \ + _x = get_cycles(); \ + call; \ + _y = get_cycles(); \ + arc_printk(D_TIMING, dev, \ + "%s: %d bytes in %lu cycles == %lu Kbytes/100Mcycle\n", \ + name, bytes, _y - _x, \ + 100000000 / 1024 * bytes / (_y - _x + 1)); \ + } else { \ + call; \ + } \ +} while (0) + +/* + * Time needed to reset the card - in ms (milliseconds). This works on my + * SMC PC100. I can't find a reference that tells me just how long I + * should wait. + */ +#define RESETtime (300) + +/* + * These are the max/min lengths of packet payload, not including the + * arc_hardware header, but definitely including the soft header. + * + * Note: packet sizes 254, 255, 256 are impossible because of the way + * ARCnet registers work That's why RFC1201 defines "exception" packets. + * In non-RFC1201 protocols, we have to just tack some extra bytes on the + * end. + */ +#define MTU 253 /* normal packet max size */ +#define MinTU 257 /* extended packet min size */ +#define XMTU 508 /* extended packet max size */ + +/* status/interrupt mask bit fields */ +#define TXFREEflag 0x01 /* transmitter available */ +#define TXACKflag 0x02 /* transmitted msg. ackd */ +#define RECONflag 0x04 /* network reconfigured */ +#define TESTflag 0x08 /* test flag */ +#define EXCNAKflag 0x08 /* excesive nak flag */ +#define RESETflag 0x10 /* power-on-reset */ +#define RES1flag 0x20 /* reserved - usually set by jumper */ +#define RES2flag 0x40 /* reserved - usually set by jumper */ +#define NORXflag 0x80 /* receiver inhibited */ + +/* Flags used for IO-mapped memory operations */ +#define AUTOINCflag 0x40 /* Increase location with each access */ +#define IOMAPflag 0x02 /* (for 90xx) Use IO mapped memory, not mmap */ +#define ENABLE16flag 0x80 /* (for 90xx) Enable 16-bit mode */ + +/* in the command register, the following bits have these meanings: + * 0-2 command + * 3-4 page number (for enable rcv/xmt command) + * 7 receive broadcasts + */ +#define NOTXcmd 0x01 /* disable transmitter */ +#define NORXcmd 0x02 /* disable receiver */ +#define TXcmd 0x03 /* enable transmitter */ +#define RXcmd 0x04 /* enable receiver */ +#define CONFIGcmd 0x05 /* define configuration */ +#define CFLAGScmd 0x06 /* clear flags */ +#define TESTcmd 0x07 /* load test flags */ +#define STARTIOcmd 0x18 /* start internal operation */ + +/* flags for "clear flags" command */ +#define RESETclear 0x08 /* power-on-reset */ +#define CONFIGclear 0x10 /* system reconfigured */ + +#define EXCNAKclear 0x0E /* Clear and acknowledge the excive nak bit */ + +/* flags for "load test flags" command */ +#define TESTload 0x08 /* test flag (diagnostic) */ + +/* byte deposited into first address of buffers on reset */ +#define TESTvalue 0321 /* that's octal for 0xD1 :) */ + +/* for "enable receiver" command */ +#define RXbcasts 0x80 /* receive broadcasts */ + +/* flags for "define configuration" command */ +#define NORMALconf 0x00 /* 1-249 byte packets */ +#define EXTconf 0x08 /* 250-504 byte packets */ + +/* card feature flags, set during auto-detection. + * (currently only used by com20020pci) + */ +#define ARC_IS_5MBIT 1 /* card default speed is 5MBit */ +#define ARC_CAN_10MBIT 2 /* card uses COM20022, supporting 10MBit, + but default is 2.5MBit. */ + +/* information needed to define an encapsulation driver */ +struct ArcProto { + char suffix; /* a for RFC1201, e for ether-encap, etc. */ + int mtu; /* largest possible packet */ + int is_ip; /* This is a ip plugin - not a raw thing */ + + void (*rx)(struct net_device *dev, int bufnum, + struct archdr *pkthdr, int length); + int (*build_header)(struct sk_buff *skb, struct net_device *dev, + unsigned short ethproto, uint8_t daddr); + + /* these functions return '1' if the skb can now be freed */ + int (*prepare_tx)(struct net_device *dev, struct archdr *pkt, + int length, int bufnum); + int (*continue_tx)(struct net_device *dev, int bufnum); + int (*ack_tx)(struct net_device *dev, int acked); +}; + +extern struct ArcProto *arc_proto_map[256], *arc_proto_default, + *arc_bcast_proto, *arc_raw_proto; + +/* + * "Incoming" is information needed for each address that could be sending + * to us. Mostly for partially-received split packets. + */ +struct Incoming { + struct sk_buff *skb; /* packet data buffer */ + __be16 sequence; /* sequence number of assembly */ + uint8_t lastpacket, /* number of last packet (from 1) */ + numpackets; /* number of packets in split */ +}; + +/* only needed for RFC1201 */ +struct Outgoing { + struct ArcProto *proto; /* protocol driver that owns this: + * if NULL, no packet is pending. + */ + struct sk_buff *skb; /* buffer from upper levels */ + struct archdr *pkt; /* a pointer into the skb */ + uint16_t length, /* bytes total */ + dataleft, /* bytes left */ + segnum, /* segment being sent */ + numsegs; /* number of segments */ +}; + +struct arcnet_local { + uint8_t config, /* current value of CONFIG register */ + timeout, /* Extended timeout for COM20020 */ + backplane, /* Backplane flag for COM20020 */ + clockp, /* COM20020 clock divider */ + clockm, /* COM20020 clock multiplier flag */ + setup, /* Contents of setup1 register */ + setup2, /* Contents of setup2 register */ + intmask; /* current value of INTMASK register */ + uint8_t default_proto[256]; /* default encap to use for each host */ + int cur_tx, /* buffer used by current transmit, or -1 */ + next_tx, /* buffer where a packet is ready to send */ + cur_rx; /* current receive buffer */ + int lastload_dest, /* can last loaded packet be acked? */ + lasttrans_dest; /* can last TX'd packet be acked? */ + int timed_out; /* need to process TX timeout and drop packet */ + unsigned long last_timeout; /* time of last reported timeout */ + char *card_name; /* card ident string */ + int card_flags; /* special card features */ + + /* On preemtive and SMB a lock is needed */ + spinlock_t lock; + + /* + * Buffer management: an ARCnet card has 4 x 512-byte buffers, each of + * which can be used for either sending or receiving. The new dynamic + * buffer management routines use a simple circular queue of available + * buffers, and take them as they're needed. This way, we simplify + * situations in which we (for example) want to pre-load a transmit + * buffer, or start receiving while we copy a received packet to + * memory. + * + * The rules: only the interrupt handler is allowed to _add_ buffers to + * the queue; thus, this doesn't require a lock. Both the interrupt + * handler and the transmit function will want to _remove_ buffers, so + * we need to handle the situation where they try to do it at the same + * time. + * + * If next_buf == first_free_buf, the queue is empty. Since there are + * only four possible buffers, the queue should never be full. + */ + atomic_t buf_lock; + int buf_queue[5]; + int next_buf, first_free_buf; + + /* network "reconfiguration" handling */ + unsigned long first_recon; /* time of "first" RECON message to count */ + unsigned long last_recon; /* time of most recent RECON */ + int num_recons; /* number of RECONs between first and last. */ + int network_down; /* do we think the network is down? */ + + int excnak_pending; /* We just got an excesive nak interrupt */ + + struct { + uint16_t sequence; /* sequence number (incs with each packet) */ + __be16 aborted_seq; + + struct Incoming incoming[256]; /* one from each address */ + } rfc1201; + + /* really only used by rfc1201, but we'll pretend it's not */ + struct Outgoing outgoing; /* packet currently being sent */ + + /* hardware-specific functions */ + struct { + struct module *owner; + void (*command)(struct net_device *dev, int cmd); + int (*status)(struct net_device *dev); + void (*intmask)(struct net_device *dev, int mask); + int (*reset)(struct net_device *dev, int really_reset); + void (*open)(struct net_device *dev); + void (*close)(struct net_device *dev); + + void (*copy_to_card)(struct net_device *dev, int bufnum, + int offset, void *buf, int count); + void (*copy_from_card)(struct net_device *dev, int bufnum, + int offset, void *buf, int count); + } hw; + + void __iomem *mem_start; /* pointer to ioremap'ed MMIO */ +}; + +#if ARCNET_DEBUG_MAX & D_SKB +void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc); +#else +static inline +void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc) +{ +} +#endif + +void arcnet_unregister_proto(struct ArcProto *proto); +irqreturn_t arcnet_interrupt(int irq, void *dev_id); +struct net_device *alloc_arcdev(const char *name); + +int arcnet_open(struct net_device *dev); +int arcnet_close(struct net_device *dev); +netdev_tx_t arcnet_send_packet(struct sk_buff *skb, + struct net_device *dev); +void arcnet_timeout(struct net_device *dev); + +/* I/O equivalents */ + +#ifdef CONFIG_SA1100_CT6001 +#define BUS_ALIGN 2 /* 8 bit device on a 16 bit bus - needs padding */ +#else +#define BUS_ALIGN 1 +#endif + +/* addr and offset allow register like names to define the actual IO address. + * A configuration option multiplies the offset for alignment. + */ +#define arcnet_inb(addr, offset) \ + inb((addr) + BUS_ALIGN * (offset)) +#define arcnet_outb(value, addr, offset) \ + outb(value, (addr) + BUS_ALIGN * (offset)) + +#define arcnet_insb(addr, offset, buffer, count) \ + insb((addr) + BUS_ALIGN * (offset), buffer, count) +#define arcnet_outsb(addr, offset, buffer, count) \ + outsb((addr) + BUS_ALIGN * (offset), buffer, count) + +#define arcnet_readb(addr, offset) \ + readb((addr) + (offset)) +#define arcnet_writeb(value, addr, offset) \ + writeb(value, (addr) + (offset)) + +#endif /* __KERNEL__ */ +#endif /* _LINUX_ARCDEVICE_H */ diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index 10f71c732b59..e41dd36fe832 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -1,6 +1,6 @@ /* * Linux ARCnet driver - device-independent routines - * + * * Written 1997 by David Woodhouse. * Written 1994-1999 by Avery Pennarun. * Written 1999-2000 by Martin Mares <mj@ucw.cz>. @@ -20,12 +20,12 @@ * modified by SRC, incorporated herein by reference. * * ********************** - * + * * The change log is now in a file called ChangeLog in this directory. * * Sources: * - Crynwr arcnet.com/arcether.com packet drivers. - * - arcnet.c v0.00 dated 1/1/94 and apparently by + * - arcnet.c v0.00 dated 1/1/94 and apparently by * Donald Becker - it didn't work :) * - skeleton.c v0.05 dated 11/16/93 by Donald Becker * (from Linux Kernel 1.1.45) @@ -41,7 +41,7 @@ * <jojo@repas.de> */ -#define VERSION "arcnet: v3.94 BETA 2007/02/08 - by Avery Pennarun et al.\n" +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/types.h> @@ -50,9 +50,11 @@ #include <linux/if_arp.h> #include <net/arp.h> #include <linux/init.h> -#include <linux/arcdevice.h> #include <linux/jiffies.h> +#include "arcdevice.h" +#include "com9026.h" + /* "do nothing" functions for protocol drivers */ static void null_rx(struct net_device *dev, int bufnum, struct archdr *pkthdr, int length); @@ -63,17 +65,24 @@ static int null_prepare_tx(struct net_device *dev, struct archdr *pkt, static void arcnet_rx(struct net_device *dev, int bufnum); -/* - * one ArcProto per possible proto ID. None of the elements of +/* one ArcProto per possible proto ID. None of the elements of * arc_proto_map are allowed to be NULL; they will get set to * arc_proto_default instead. It also must not be NULL; if you would like * to set it to NULL, set it to &arc_proto_null instead. */ - struct ArcProto *arc_proto_map[256], *arc_proto_default, - *arc_bcast_proto, *arc_raw_proto; +struct ArcProto *arc_proto_map[256]; +EXPORT_SYMBOL(arc_proto_map); -static struct ArcProto arc_proto_null = -{ +struct ArcProto *arc_proto_default; +EXPORT_SYMBOL(arc_proto_default); + +struct ArcProto *arc_bcast_proto; +EXPORT_SYMBOL(arc_bcast_proto); + +struct ArcProto *arc_raw_proto; +EXPORT_SYMBOL(arc_raw_proto); + +static struct ArcProto arc_proto_null = { .suffix = '?', .mtu = XMTU, .is_ip = 0, @@ -86,19 +95,7 @@ static struct ArcProto arc_proto_null = /* Exported function prototypes */ int arcnet_debug = ARCNET_DEBUG; - -EXPORT_SYMBOL(arc_proto_map); -EXPORT_SYMBOL(arc_proto_default); -EXPORT_SYMBOL(arc_bcast_proto); -EXPORT_SYMBOL(arc_raw_proto); -EXPORT_SYMBOL(arcnet_unregister_proto); EXPORT_SYMBOL(arcnet_debug); -EXPORT_SYMBOL(alloc_arcdev); -EXPORT_SYMBOL(arcnet_interrupt); -EXPORT_SYMBOL(arcnet_open); -EXPORT_SYMBOL(arcnet_close); -EXPORT_SYMBOL(arcnet_send_packet); -EXPORT_SYMBOL(arcnet_timeout); /* Internal function prototypes */ static int arcnet_header(struct sk_buff *skb, struct net_device *dev, @@ -116,29 +113,20 @@ static int __init arcnet_init(void) arcnet_debug = debug; - printk("arcnet loaded.\n"); - -#ifdef ALPHA_WARNING - BUGLVL(D_EXTRA) { - printk("arcnet: ***\n" - "arcnet: * Read arcnet.txt for important release notes!\n" - "arcnet: *\n" - "arcnet: * This is an ALPHA version! (Last stable release: v3.02) E-mail\n" - "arcnet: * me if you have any questions, comments, or bug reports.\n" - "arcnet: ***\n"); - } -#endif + pr_info("arcnet loaded\n"); /* initialize the protocol map */ arc_raw_proto = arc_proto_default = arc_bcast_proto = &arc_proto_null; for (count = 0; count < 256; count++) arc_proto_map[count] = arc_proto_default; - BUGLVL(D_DURING) - printk("arcnet: struct sizes: %Zd %Zd %Zd %Zd %Zd\n", - sizeof(struct arc_hardware), sizeof(struct arc_rfc1201), - sizeof(struct arc_rfc1051), sizeof(struct arc_eth_encap), - sizeof(struct archdr)); + if (BUGLVL(D_DURING)) + pr_info("struct sizes: %Zd %Zd %Zd %Zd %Zd\n", + sizeof(struct arc_hardware), + sizeof(struct arc_rfc1201), + sizeof(struct arc_rfc1051), + sizeof(struct arc_eth_encap), + sizeof(struct archdr)); return 0; } @@ -150,9 +138,7 @@ static void __exit arcnet_exit(void) module_init(arcnet_init); module_exit(arcnet_exit); -/* - * Dump the contents of an sk_buff - */ +/* Dump the contents of an sk_buff */ #if ARCNET_DEBUG_MAX & D_SKB void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc) @@ -164,14 +150,10 @@ void arcnet_dump_skb(struct net_device *dev, print_hex_dump(KERN_DEBUG, hdr, DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb->len, true); } - EXPORT_SYMBOL(arcnet_dump_skb); #endif - -/* - * Dump the contents of an ARCnet buffer - */ +/* Dump the contents of an ARCnet buffer */ #if (ARCNET_DEBUG_MAX & (D_RX | D_TX)) static void arcnet_dump_packet(struct net_device *dev, int bufnum, char *desc, int take_arcnet_lock) @@ -183,12 +165,13 @@ static void arcnet_dump_packet(struct net_device *dev, int bufnum, char hdr[32]; /* hw.copy_from_card expects IRQ context so take the IRQ lock - to keep it single threaded */ - if(take_arcnet_lock) + * to keep it single threaded + */ + if (take_arcnet_lock) spin_lock_irqsave(&lp->lock, flags); lp->hw.copy_from_card(dev, bufnum, 0, buf, 512); - if(take_arcnet_lock) + if (take_arcnet_lock) spin_unlock_irqrestore(&lp->lock, flags); /* if the offset[0] byte is nonzero, this is a 256-byte packet */ @@ -202,13 +185,11 @@ static void arcnet_dump_packet(struct net_device *dev, int bufnum, #else -#define arcnet_dump_packet(dev, bufnum, desc,take_arcnet_lock) do { } while (0) +#define arcnet_dump_packet(dev, bufnum, desc, take_arcnet_lock) do { } while (0) #endif - -/* - * Unregister a protocol driver from the arc_proto_map. Protocol drivers +/* Unregister a protocol driver from the arc_proto_map. Protocol drivers * are responsible for registering themselves, but the unregister routine * is pretty generic so we'll do it here. */ @@ -228,12 +209,11 @@ void arcnet_unregister_proto(struct ArcProto *proto) arc_proto_map[count] = arc_proto_default; } } +EXPORT_SYMBOL(arcnet_unregister_proto); - -/* - * Add a buffer to the queue. Only the interrupt handler is allowed to do +/* Add a buffer to the queue. Only the interrupt handler is allowed to do * this, unless interrupts are disabled. - * + * * Note: we don't check for a full queue, since there aren't enough buffers * to more than fill it. */ @@ -245,19 +225,17 @@ static void release_arcbuf(struct net_device *dev, int bufnum) lp->buf_queue[lp->first_free_buf++] = bufnum; lp->first_free_buf %= 5; - BUGLVL(D_DURING) { - BUGMSG(D_DURING, "release_arcbuf: freed #%d; buffer queue is now: ", - bufnum); - for (i = lp->next_buf; i != lp->first_free_buf; i = (i+1) % 5) - BUGMSG2(D_DURING, "#%d ", lp->buf_queue[i]); - BUGMSG2(D_DURING, "\n"); + if (BUGLVL(D_DURING)) { + arc_printk(D_DURING, dev, "release_arcbuf: freed #%d; buffer queue is now: ", + bufnum); + for (i = lp->next_buf; i != lp->first_free_buf; i = (i + 1) % 5) + arc_cont(D_DURING, "#%d ", lp->buf_queue[i]); + arc_cont(D_DURING, "\n"); } } - -/* - * Get a buffer from the queue. If this returns -1, there are no buffers - * available. +/* Get a buffer from the queue. + * If this returns -1, there are no buffers available. */ static int get_arcbuf(struct net_device *dev) { @@ -266,34 +244,32 @@ static int get_arcbuf(struct net_device *dev) if (!atomic_dec_and_test(&lp->buf_lock)) { /* already in this function */ - BUGMSG(D_NORMAL, "get_arcbuf: overlap (%d)!\n", - lp->buf_lock.counter); - } - else { /* we can continue */ + arc_printk(D_NORMAL, dev, "get_arcbuf: overlap (%d)!\n", + lp->buf_lock.counter); + } else { /* we can continue */ if (lp->next_buf >= 5) lp->next_buf -= 5; - if (lp->next_buf == lp->first_free_buf) - BUGMSG(D_NORMAL, "get_arcbuf: BUG: no buffers are available??\n"); - else { + if (lp->next_buf == lp->first_free_buf) { + arc_printk(D_NORMAL, dev, "get_arcbuf: BUG: no buffers are available??\n"); + } else { buf = lp->buf_queue[lp->next_buf++]; lp->next_buf %= 5; } } - - BUGLVL(D_DURING) { - BUGMSG(D_DURING, "get_arcbuf: got #%d; buffer queue is now: ", buf); - for (i = lp->next_buf; i != lp->first_free_buf; i = (i+1) % 5) - BUGMSG2(D_DURING, "#%d ", lp->buf_queue[i]); - BUGMSG2(D_DURING, "\n"); + if (BUGLVL(D_DURING)) { + arc_printk(D_DURING, dev, "get_arcbuf: got #%d; buffer queue is now: ", + buf); + for (i = lp->next_buf; i != lp->first_free_buf; i = (i + 1) % 5) + arc_cont(D_DURING, "#%d ", lp->buf_queue[i]); + arc_cont(D_DURING, "\n"); } atomic_inc(&lp->buf_lock); return buf; } - static int choose_mtu(void) { int count, mtu = 65535; @@ -326,7 +302,7 @@ static void arcdev_setup(struct net_device *dev) dev->type = ARPHRD_ARCNET; dev->netdev_ops = &arcnet_netdev_ops; dev->header_ops = &arcnet_header_ops; - dev->hard_header_len = sizeof(struct archdr); + dev->hard_header_len = sizeof(struct arc_hardware); dev->mtu = choose_mtu(); dev->addr_len = ARCNET_ALEN; @@ -336,7 +312,6 @@ static void arcdev_setup(struct net_device *dev) /* New-style flags. */ dev->flags = IFF_BROADCAST; - } struct net_device *alloc_arcdev(const char *name) @@ -346,16 +321,17 @@ struct net_device *alloc_arcdev(const char *name) dev = alloc_netdev(sizeof(struct arcnet_local), name && *name ? name : "arc%d", NET_NAME_UNKNOWN, arcdev_setup); - if(dev) { + if (dev) { struct arcnet_local *lp = netdev_priv(dev); + spin_lock_init(&lp->lock); } return dev; } +EXPORT_SYMBOL(alloc_arcdev); -/* - * Open/initialize the board. This is called sometime after booting when +/* Open/initialize the board. This is called sometime after booting when * the 'ifconfig' program is run. * * This routine should set everything up anew at each open, even registers @@ -367,34 +343,33 @@ int arcnet_open(struct net_device *dev) struct arcnet_local *lp = netdev_priv(dev); int count, newmtu, error; - BUGMSG(D_INIT,"opened."); + arc_printk(D_INIT, dev, "opened."); if (!try_module_get(lp->hw.owner)) return -ENODEV; - BUGLVL(D_PROTO) { - BUGMSG(D_PROTO, "protocol map (default is '%c'): ", - arc_proto_default->suffix); + if (BUGLVL(D_PROTO)) { + arc_printk(D_PROTO, dev, "protocol map (default is '%c'): ", + arc_proto_default->suffix); for (count = 0; count < 256; count++) - BUGMSG2(D_PROTO, "%c", arc_proto_map[count]->suffix); - BUGMSG2(D_PROTO, "\n"); + arc_cont(D_PROTO, "%c", arc_proto_map[count]->suffix); + arc_cont(D_PROTO, "\n"); } - - BUGMSG(D_INIT, "arcnet_open: resetting card.\n"); + arc_printk(D_INIT, dev, "arcnet_open: resetting card.\n"); /* try to put the card in a defined state - if it fails the first * time, actually reset it. */ error = -ENODEV; - if (ARCRESET(0) && ARCRESET(1)) + if (lp->hw.reset(dev, 0) && lp->hw.reset(dev, 1)) goto out_module_put; newmtu = choose_mtu(); if (newmtu < dev->mtu) dev->mtu = newmtu; - BUGMSG(D_INIT, "arcnet_open: mtu: %d.\n", dev->mtu); + arc_printk(D_INIT, dev, "arcnet_open: mtu: %d.\n", dev->mtu); /* autodetect the encapsulation for each host. */ memset(lp->default_proto, 0, sizeof(lp->default_proto)); @@ -425,30 +400,28 @@ int arcnet_open(struct net_device *dev) lp->hw.open(dev); if (dev->dev_addr[0] == 0) - BUGMSG(D_NORMAL, "WARNING! Station address 00 is reserved " - "for broadcasts!\n"); + arc_printk(D_NORMAL, dev, "WARNING! Station address 00 is reserved for broadcasts!\n"); else if (dev->dev_addr[0] == 255) - BUGMSG(D_NORMAL, "WARNING! Station address FF may confuse " - "DOS networking programs!\n"); + arc_printk(D_NORMAL, dev, "WARNING! Station address FF may confuse DOS networking programs!\n"); - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); - if (ASTATUS() & RESETflag) { - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); - ACOMMAND(CFLAGScmd | RESETclear); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + if (lp->hw.status(dev) & RESETflag) { + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", + __FILE__, __LINE__, __func__); + lp->hw.command(dev, CFLAGScmd | RESETclear); } - - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); /* make sure we're ready to receive IRQ's. */ - AINTMASK(0); + lp->hw.intmask(dev, 0); udelay(1); /* give it time to set the mask before * we reset it again. (may not even be * necessary) */ - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); lp->intmask = NORXflag | RECONflag; - AINTMASK(lp->intmask); - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + lp->hw.intmask(dev, lp->intmask); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); netif_start_queue(dev); @@ -458,7 +431,7 @@ int arcnet_open(struct net_device *dev) module_put(lp->hw.owner); return error; } - +EXPORT_SYMBOL(arcnet_open); /* The inverse routine to arcnet_open - shuts down the card. */ int arcnet_close(struct net_device *dev) @@ -468,9 +441,9 @@ int arcnet_close(struct net_device *dev) netif_stop_queue(dev); /* flush TX and disable RX */ - AINTMASK(0); - ACOMMAND(NOTXcmd); /* stop transmit */ - ACOMMAND(NORXcmd); /* disable receive */ + lp->hw.intmask(dev, 0); + lp->hw.command(dev, NOTXcmd); /* stop transmit */ + lp->hw.command(dev, NORXcmd); /* disable receive */ mdelay(1); /* shut down the card */ @@ -478,7 +451,7 @@ int arcnet_close(struct net_device *dev) module_put(lp->hw.owner); return 0; } - +EXPORT_SYMBOL(arcnet_close); static int arcnet_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, @@ -488,48 +461,44 @@ static int arcnet_header(struct sk_buff *skb, struct net_device *dev, uint8_t _daddr, proto_num; struct ArcProto *proto; - BUGMSG(D_DURING, - "create header from %d to %d; protocol %d (%Xh); size %u.\n", - saddr ? *(uint8_t *) saddr : -1, - daddr ? *(uint8_t *) daddr : -1, - type, type, len); - - if (skb->len!=0 && len != skb->len) - BUGMSG(D_NORMAL, "arcnet_header: Yikes! skb->len(%d) != len(%d)!\n", - skb->len, len); - - - /* Type is host order - ? */ - if(type == ETH_P_ARCNET) { - proto = arc_raw_proto; - BUGMSG(D_DEBUG, "arc_raw_proto used. proto='%c'\n",proto->suffix); - _daddr = daddr ? *(uint8_t *) daddr : 0; - } - else if (!daddr) { - /* - * if the dest addr isn't provided, we can't choose an encapsulation! - * Store the packet type (eg. ETH_P_IP) for now, and we'll push on a - * real header when we do rebuild_header. - */ - *(uint16_t *) skb_push(skb, 2) = type; - /* - * XXX: Why not use skb->mac_len? + arc_printk(D_DURING, dev, + "create header from %d to %d; protocol %d (%Xh); size %u.\n", + saddr ? *(uint8_t *)saddr : -1, + daddr ? *(uint8_t *)daddr : -1, + type, type, len); + + if (skb->len != 0 && len != skb->len) + arc_printk(D_NORMAL, dev, "arcnet_header: Yikes! skb->len(%d) != len(%d)!\n", + skb->len, len); + + /* Type is host order - ? */ + if (type == ETH_P_ARCNET) { + proto = arc_raw_proto; + arc_printk(D_DEBUG, dev, "arc_raw_proto used. proto='%c'\n", + proto->suffix); + _daddr = daddr ? *(uint8_t *)daddr : 0; + } else if (!daddr) { + /* if the dest addr isn't provided, we can't choose an + * encapsulation! Store the packet type (eg. ETH_P_IP) + * for now, and we'll push on a real header when we do + * rebuild_header. */ + *(uint16_t *)skb_push(skb, 2) = type; + /* XXX: Why not use skb->mac_len? */ if (skb->network_header - skb->mac_header != 2) - BUGMSG(D_NORMAL, "arcnet_header: Yikes! diff (%d) is not 2!\n", - (int)(skb->network_header - skb->mac_header)); + arc_printk(D_NORMAL, dev, "arcnet_header: Yikes! diff (%u) is not 2!\n", + skb->network_header - skb->mac_header); return -2; /* return error -- can't transmit yet! */ - } - else { + } else { /* otherwise, we can just add the header as usual. */ - _daddr = *(uint8_t *) daddr; + _daddr = *(uint8_t *)daddr; proto_num = lp->default_proto[_daddr]; proto = arc_proto_map[proto_num]; - BUGMSG(D_DURING, "building header for %02Xh using protocol '%c'\n", - proto_num, proto->suffix); + arc_printk(D_DURING, dev, "building header for %02Xh using protocol '%c'\n", + proto_num, proto->suffix); if (proto == &arc_proto_null && arc_bcast_proto != proto) { - BUGMSG(D_DURING, "actually, let's use '%c' instead.\n", - arc_bcast_proto->suffix); + arc_printk(D_DURING, dev, "actually, let's use '%c' instead.\n", + arc_bcast_proto->suffix); proto = arc_bcast_proto; } } @@ -538,7 +507,7 @@ static int arcnet_header(struct sk_buff *skb, struct net_device *dev, /* Called by the kernel in order to transmit a packet. */ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, - struct net_device *dev) + struct net_device *dev) { struct arcnet_local *lp = netdev_priv(dev); struct archdr *pkt; @@ -548,21 +517,22 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, unsigned long flags; int freeskb, retval; - BUGMSG(D_DURING, - "transmit requested (status=%Xh, txbufs=%d/%d, len=%d, protocol %x)\n", - ASTATUS(), lp->cur_tx, lp->next_tx, skb->len,skb->protocol); + arc_printk(D_DURING, dev, + "transmit requested (status=%Xh, txbufs=%d/%d, len=%d, protocol %x)\n", + lp->hw.status(dev), lp->cur_tx, lp->next_tx, skb->len, skb->protocol); - pkt = (struct archdr *) skb->data; + pkt = (struct archdr *)skb->data; soft = &pkt->soft.rfc1201; proto = arc_proto_map[soft->proto]; - BUGMSG(D_SKB_SIZE, "skb: transmitting %d bytes to %02X\n", - skb->len, pkt->hard.dest); - BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "tx"); + arc_printk(D_SKB_SIZE, dev, "skb: transmitting %d bytes to %02X\n", + skb->len, pkt->hard.dest); + if (BUGLVL(D_SKB)) + arcnet_dump_skb(dev, skb, "tx"); /* fits in one packet? */ if (skb->len - ARC_HDR_SIZE > XMTU && !proto->continue_tx) { - BUGMSG(D_NORMAL, "fixme: packet too large: compensating badly!\n"); + arc_printk(D_NORMAL, dev, "fixme: packet too large: compensating badly!\n"); dev_kfree_skb(skb); return NETDEV_TX_OK; /* don't try again */ } @@ -571,17 +541,18 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, netif_stop_queue(dev); spin_lock_irqsave(&lp->lock, flags); - AINTMASK(0); - if(lp->next_tx == -1) + lp->hw.intmask(dev, 0); + if (lp->next_tx == -1) txbuf = get_arcbuf(dev); - else { + else txbuf = -1; - } + if (txbuf != -1) { if (proto->prepare_tx(dev, pkt, skb->len, txbuf) && !proto->ack_tx) { /* done right away and we don't want to acknowledge - the package later - forget about it now */ + * the package later - forget about it now + */ dev->stats.tx_bytes += skb->len; freeskb = 1; } else { @@ -594,9 +565,9 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, if (proto->continue_tx && proto->continue_tx(dev, txbuf)) { - BUGMSG(D_NORMAL, - "bug! continue_tx finished the first time! " - "(proto='%c')\n", proto->suffix); + arc_printk(D_NORMAL, dev, + "bug! continue_tx finished the first time! (proto='%c')\n", + proto->suffix); } } retval = NETDEV_TX_OK; @@ -606,61 +577,62 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, freeskb = 0; } - BUGMSG(D_DEBUG, "%s: %d: %s, status: %x\n",__FILE__,__LINE__,__func__,ASTATUS()); + arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n", + __FILE__, __LINE__, __func__, lp->hw.status(dev)); /* make sure we didn't ignore a TX IRQ while we were in here */ - AINTMASK(0); + lp->hw.intmask(dev, 0); - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); - lp->intmask |= TXFREEflag|EXCNAKflag; - AINTMASK(lp->intmask); - BUGMSG(D_DEBUG, "%s: %d: %s, status: %x\n",__FILE__,__LINE__,__func__,ASTATUS()); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + lp->intmask |= TXFREEflag | EXCNAKflag; + lp->hw.intmask(dev, lp->intmask); + arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n", + __FILE__, __LINE__, __func__, lp->hw.status(dev)); spin_unlock_irqrestore(&lp->lock, flags); - if (freeskb) { + if (freeskb) dev_kfree_skb(skb); - } + return retval; /* no need to try again */ } +EXPORT_SYMBOL(arcnet_send_packet); - -/* - * Actually start transmitting a packet that was loaded into a buffer +/* Actually start transmitting a packet that was loaded into a buffer * by prepare_tx. This should _only_ be called by the interrupt handler. */ static int go_tx(struct net_device *dev) { struct arcnet_local *lp = netdev_priv(dev); - BUGMSG(D_DURING, "go_tx: status=%Xh, intmask=%Xh, next_tx=%d, cur_tx=%d\n", - ASTATUS(), lp->intmask, lp->next_tx, lp->cur_tx); + arc_printk(D_DURING, dev, "go_tx: status=%Xh, intmask=%Xh, next_tx=%d, cur_tx=%d\n", + lp->hw.status(dev), lp->intmask, lp->next_tx, lp->cur_tx); if (lp->cur_tx != -1 || lp->next_tx == -1) return 0; - BUGLVL(D_TX) arcnet_dump_packet(dev, lp->next_tx, "go_tx", 0); + if (BUGLVL(D_TX)) + arcnet_dump_packet(dev, lp->next_tx, "go_tx", 0); lp->cur_tx = lp->next_tx; lp->next_tx = -1; /* start sending */ - ACOMMAND(TXcmd | (lp->cur_tx << 3)); + lp->hw.command(dev, TXcmd | (lp->cur_tx << 3)); dev->stats.tx_packets++; lp->lasttrans_dest = lp->lastload_dest; lp->lastload_dest = 0; lp->excnak_pending = 0; - lp->intmask |= TXFREEflag|EXCNAKflag; + lp->intmask |= TXFREEflag | EXCNAKflag; return 1; } - /* Called by the kernel when transmit times out */ void arcnet_timeout(struct net_device *dev) { unsigned long flags; struct arcnet_local *lp = netdev_priv(dev); - int status = ASTATUS(); + int status = lp->hw.status(dev); char *msg; spin_lock_irqsave(&lp->lock, flags); @@ -670,30 +642,29 @@ void arcnet_timeout(struct net_device *dev) msg = ""; dev->stats.tx_aborted_errors++; lp->timed_out = 1; - ACOMMAND(NOTXcmd | (lp->cur_tx << 3)); + lp->hw.command(dev, NOTXcmd | (lp->cur_tx << 3)); } dev->stats.tx_errors++; /* make sure we didn't miss a TX or a EXC NAK IRQ */ - AINTMASK(0); - lp->intmask |= TXFREEflag|EXCNAKflag; - AINTMASK(lp->intmask); - + lp->hw.intmask(dev, 0); + lp->intmask |= TXFREEflag | EXCNAKflag; + lp->hw.intmask(dev, lp->intmask); + spin_unlock_irqrestore(&lp->lock, flags); - if (time_after(jiffies, lp->last_timeout + 10*HZ)) { - BUGMSG(D_EXTRA, "tx timed out%s (status=%Xh, intmask=%Xh, dest=%02Xh)\n", - msg, status, lp->intmask, lp->lasttrans_dest); + if (time_after(jiffies, lp->last_timeout + 10 * HZ)) { + arc_printk(D_EXTRA, dev, "tx timed out%s (status=%Xh, intmask=%Xh, dest=%02Xh)\n", + msg, status, lp->intmask, lp->lasttrans_dest); lp->last_timeout = jiffies; } if (lp->cur_tx == -1) netif_wake_queue(dev); } +EXPORT_SYMBOL(arcnet_timeout); - -/* - * The typical workload of the driver: Handle the network interface +/* The typical workload of the driver: Handle the network interface * interrupts. Establish which device needs attention, and call the correct * chipset interrupt handler. */ @@ -704,125 +675,125 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id) int recbuf, status, diagstatus, didsomething, boguscount; int retval = IRQ_NONE; - BUGMSG(D_DURING, "\n"); + arc_printk(D_DURING, dev, "\n"); - BUGMSG(D_DURING, "in arcnet_interrupt\n"); + arc_printk(D_DURING, dev, "in arcnet_interrupt\n"); lp = netdev_priv(dev); BUG_ON(!lp); - + spin_lock(&lp->lock); - /* - * RESET flag was enabled - if device is not running, we must clear it right - * away (but nothing else). + /* RESET flag was enabled - if device is not running, we must + * clear it right away (but nothing else). */ if (!netif_running(dev)) { - if (ASTATUS() & RESETflag) - ACOMMAND(CFLAGScmd | RESETclear); - AINTMASK(0); + if (lp->hw.status(dev) & RESETflag) + lp->hw.command(dev, CFLAGScmd | RESETclear); + lp->hw.intmask(dev, 0); spin_unlock(&lp->lock); return retval; } - BUGMSG(D_DURING, "in arcnet_inthandler (status=%Xh, intmask=%Xh)\n", - ASTATUS(), lp->intmask); + arc_printk(D_DURING, dev, "in arcnet_inthandler (status=%Xh, intmask=%Xh)\n", + lp->hw.status(dev), lp->intmask); boguscount = 5; do { - status = ASTATUS(); - diagstatus = (status >> 8) & 0xFF; + status = lp->hw.status(dev); + diagstatus = (status >> 8) & 0xFF; - BUGMSG(D_DEBUG, "%s: %d: %s: status=%x\n", - __FILE__,__LINE__,__func__,status); + arc_printk(D_DEBUG, dev, "%s: %d: %s: status=%x\n", + __FILE__, __LINE__, __func__, status); didsomething = 0; - /* - * RESET flag was enabled - card is resetting and if RX is + /* RESET flag was enabled - card is resetting and if RX is * disabled, it's NOT because we just got a packet. - * - * The card is in an undefined state. Clear it out and start over. + * + * The card is in an undefined state. + * Clear it out and start over. */ if (status & RESETflag) { - BUGMSG(D_NORMAL, "spurious reset (status=%Xh)\n", status); + arc_printk(D_NORMAL, dev, "spurious reset (status=%Xh)\n", + status); arcnet_close(dev); arcnet_open(dev); /* get out of the interrupt handler! */ break; } - /* - * RX is inhibited - we must have received something. Prepare to - * receive into the next buffer. - * - * We don't actually copy the received packet from the card until - * after the transmit handler runs (and possibly launches the next - * tx); this should improve latency slightly if we get both types - * of interrupts at once. + /* RX is inhibited - we must have received something. + * Prepare to receive into the next buffer. + * + * We don't actually copy the received packet from the card + * until after the transmit handler runs (and possibly + * launches the next tx); this should improve latency slightly + * if we get both types of interrupts at once. */ recbuf = -1; if (status & lp->intmask & NORXflag) { recbuf = lp->cur_rx; - BUGMSG(D_DURING, "Buffer #%d: receive irq (status=%Xh)\n", - recbuf, status); + arc_printk(D_DURING, dev, "Buffer #%d: receive irq (status=%Xh)\n", + recbuf, status); lp->cur_rx = get_arcbuf(dev); if (lp->cur_rx != -1) { - BUGMSG(D_DURING, "enabling receive to buffer #%d\n", - lp->cur_rx); - ACOMMAND(RXcmd | (lp->cur_rx << 3) | RXbcasts); + arc_printk(D_DURING, dev, "enabling receive to buffer #%d\n", + lp->cur_rx); + lp->hw.command(dev, RXcmd | (lp->cur_rx << 3) | RXbcasts); } didsomething++; } - if((diagstatus & EXCNAKflag)) { - BUGMSG(D_DURING, "EXCNAK IRQ (diagstat=%Xh)\n", - diagstatus); + if ((diagstatus & EXCNAKflag)) { + arc_printk(D_DURING, dev, "EXCNAK IRQ (diagstat=%Xh)\n", + diagstatus); - ACOMMAND(NOTXcmd); /* disable transmit */ - lp->excnak_pending = 1; + lp->hw.command(dev, NOTXcmd); /* disable transmit */ + lp->excnak_pending = 1; - ACOMMAND(EXCNAKclear); + lp->hw.command(dev, EXCNAKclear); lp->intmask &= ~(EXCNAKflag); - didsomething++; - } - + didsomething++; + } /* a transmit finished, and we're interested in it. */ if ((status & lp->intmask & TXFREEflag) || lp->timed_out) { - lp->intmask &= ~(TXFREEflag|EXCNAKflag); + lp->intmask &= ~(TXFREEflag | EXCNAKflag); - BUGMSG(D_DURING, "TX IRQ (stat=%Xh)\n", status); + arc_printk(D_DURING, dev, "TX IRQ (stat=%Xh)\n", + status); if (lp->cur_tx != -1 && !lp->timed_out) { - if(!(status & TXACKflag)) { + if (!(status & TXACKflag)) { if (lp->lasttrans_dest != 0) { - BUGMSG(D_EXTRA, - "transmit was not acknowledged! " - "(status=%Xh, dest=%02Xh)\n", - status, lp->lasttrans_dest); + arc_printk(D_EXTRA, dev, + "transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n", + status, + lp->lasttrans_dest); dev->stats.tx_errors++; dev->stats.tx_carrier_errors++; } else { - BUGMSG(D_DURING, - "broadcast was not acknowledged; that's normal " - "(status=%Xh, dest=%02Xh)\n", - status, lp->lasttrans_dest); + arc_printk(D_DURING, dev, + "broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n", + status, + lp->lasttrans_dest); } } if (lp->outgoing.proto && lp->outgoing.proto->ack_tx) { - int ackstatus; - if(status & TXACKflag) - ackstatus=2; - else if(lp->excnak_pending) - ackstatus=1; - else - ackstatus=0; - - lp->outgoing.proto - ->ack_tx(dev, ackstatus); + int ackstatus; + + if (status & TXACKflag) + ackstatus = 2; + else if (lp->excnak_pending) + ackstatus = 1; + else + ackstatus = 0; + + lp->outgoing.proto + ->ack_tx(dev, ackstatus); } } if (lp->cur_tx != -1) @@ -836,17 +807,18 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id) go_tx(dev); /* continue a split packet, if any */ - if (lp->outgoing.proto && lp->outgoing.proto->continue_tx) { + if (lp->outgoing.proto && + lp->outgoing.proto->continue_tx) { int txbuf = get_arcbuf(dev); + if (txbuf != -1) { if (lp->outgoing.proto->continue_tx(dev, txbuf)) { /* that was the last segment */ dev->stats.tx_bytes += lp->outgoing.skb->len; - if(!lp->outgoing.proto->ack_tx) - { - dev_kfree_skb_irq(lp->outgoing.skb); - lp->outgoing.proto = NULL; - } + if (!lp->outgoing.proto->ack_tx) { + dev_kfree_skb_irq(lp->outgoing.skb); + lp->outgoing.proto = NULL; + } } lp->next_tx = txbuf; } @@ -857,7 +829,8 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id) } /* now process the received packet, if any */ if (recbuf != -1) { - BUGLVL(D_RX) arcnet_dump_packet(dev, recbuf, "rx irq", 0); + if (BUGLVL(D_RX)) + arcnet_dump_packet(dev, recbuf, "rx irq", 0); arcnet_rx(dev, recbuf); release_arcbuf(dev, recbuf); @@ -865,32 +838,32 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id) didsomething++; } if (status & lp->intmask & RECONflag) { - ACOMMAND(CFLAGScmd | CONFIGclear); + lp->hw.command(dev, CFLAGScmd | CONFIGclear); dev->stats.tx_carrier_errors++; - BUGMSG(D_RECON, "Network reconfiguration detected (status=%Xh)\n", - status); + arc_printk(D_RECON, dev, "Network reconfiguration detected (status=%Xh)\n", + status); /* MYRECON bit is at bit 7 of diagstatus */ - if(diagstatus & 0x80) - BUGMSG(D_RECON,"Put out that recon myself\n"); + if (diagstatus & 0x80) + arc_printk(D_RECON, dev, "Put out that recon myself\n"); /* is the RECON info empty or old? */ if (!lp->first_recon || !lp->last_recon || time_after(jiffies, lp->last_recon + HZ * 10)) { if (lp->network_down) - BUGMSG(D_NORMAL, "reconfiguration detected: cabling restored?\n"); + arc_printk(D_NORMAL, dev, "reconfiguration detected: cabling restored?\n"); lp->first_recon = lp->last_recon = jiffies; lp->num_recons = lp->network_down = 0; - BUGMSG(D_DURING, "recon: clearing counters.\n"); + arc_printk(D_DURING, dev, "recon: clearing counters.\n"); } else { /* add to current RECON counter */ lp->last_recon = jiffies; lp->num_recons++; - BUGMSG(D_DURING, "recon: counter=%d, time=%lds, net=%d\n", - lp->num_recons, - (lp->last_recon - lp->first_recon) / HZ, - lp->network_down); + arc_printk(D_DURING, dev, "recon: counter=%d, time=%lds, net=%d\n", + lp->num_recons, + (lp->last_recon - lp->first_recon) / HZ, + lp->network_down); /* if network is marked up; * and first_recon and last_recon are 60+ apart; @@ -902,46 +875,44 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id) (lp->last_recon - lp->first_recon) <= HZ * 60 && lp->num_recons >= RECON_THRESHOLD) { lp->network_down = 1; - BUGMSG(D_NORMAL, "many reconfigurations detected: cabling problem?\n"); + arc_printk(D_NORMAL, dev, "many reconfigurations detected: cabling problem?\n"); } else if (!lp->network_down && lp->last_recon - lp->first_recon > HZ * 60) { - /* reset counters if we've gone for over a minute. */ + /* reset counters if we've gone for + * over a minute. + */ lp->first_recon = lp->last_recon; lp->num_recons = 1; } } } else if (lp->network_down && - time_after(jiffies, lp->last_recon + HZ * 10)) { + time_after(jiffies, lp->last_recon + HZ * 10)) { if (lp->network_down) - BUGMSG(D_NORMAL, "cabling restored?\n"); + arc_printk(D_NORMAL, dev, "cabling restored?\n"); lp->first_recon = lp->last_recon = 0; lp->num_recons = lp->network_down = 0; - BUGMSG(D_DURING, "not recon: clearing counters anyway.\n"); + arc_printk(D_DURING, dev, "not recon: clearing counters anyway.\n"); } - if(didsomething) { + if (didsomething) retval |= IRQ_HANDLED; - } - } - while (--boguscount && didsomething); - - BUGMSG(D_DURING, "arcnet_interrupt complete (status=%Xh, count=%d)\n", - ASTATUS(), boguscount); - BUGMSG(D_DURING, "\n"); + } while (--boguscount && didsomething); + arc_printk(D_DURING, dev, "arcnet_interrupt complete (status=%Xh, count=%d)\n", + lp->hw.status(dev), boguscount); + arc_printk(D_DURING, dev, "\n"); - AINTMASK(0); + lp->hw.intmask(dev, 0); udelay(1); - AINTMASK(lp->intmask); - + lp->hw.intmask(dev, lp->intmask); + spin_unlock(&lp->lock); return retval; } +EXPORT_SYMBOL(arcnet_interrupt); - -/* - * This is a generic packet receiver that calls arcnet??_rx depending on the +/* This is a generic packet receiver that calls arcnet??_rx depending on the * protocol ID found. */ static void arcnet_rx(struct net_device *dev, int bufnum) @@ -963,32 +934,31 @@ static void arcnet_rx(struct net_device *dev, int bufnum) } /* get the full header, if possible */ - if (sizeof(pkt.soft) <= length) + if (sizeof(pkt.soft) <= length) { lp->hw.copy_from_card(dev, bufnum, ofs, soft, sizeof(pkt.soft)); - else { + } else { memset(&pkt.soft, 0, sizeof(pkt.soft)); lp->hw.copy_from_card(dev, bufnum, ofs, soft, length); } - BUGMSG(D_DURING, "Buffer #%d: received packet from %02Xh to %02Xh " - "(%d+4 bytes)\n", - bufnum, pkt.hard.source, pkt.hard.dest, length); + arc_printk(D_DURING, dev, "Buffer #%d: received packet from %02Xh to %02Xh (%d+4 bytes)\n", + bufnum, pkt.hard.source, pkt.hard.dest, length); dev->stats.rx_packets++; dev->stats.rx_bytes += length + ARC_HDR_SIZE; /* call the right receiver for the protocol */ if (arc_proto_map[soft->proto]->is_ip) { - BUGLVL(D_PROTO) { + if (BUGLVL(D_PROTO)) { struct ArcProto *oldp = arc_proto_map[lp->default_proto[pkt.hard.source]], *newp = arc_proto_map[soft->proto]; if (oldp != newp) { - BUGMSG(D_PROTO, - "got protocol %02Xh; encap for host %02Xh is now '%c'" - " (was '%c')\n", soft->proto, pkt.hard.source, - newp->suffix, oldp->suffix); + arc_printk(D_PROTO, dev, + "got protocol %02Xh; encap for host %02Xh is now '%c' (was '%c')\n", + soft->proto, pkt.hard.source, + newp->suffix, oldp->suffix); } } @@ -1002,30 +972,27 @@ static void arcnet_rx(struct net_device *dev, int bufnum) arc_proto_map[soft->proto]->rx(dev, bufnum, &pkt, length); } - static void null_rx(struct net_device *dev, int bufnum, struct archdr *pkthdr, int length) { - BUGMSG(D_PROTO, - "rx: don't know how to deal with proto %02Xh from host %02Xh.\n", - pkthdr->soft.rfc1201.proto, pkthdr->hard.source); + arc_printk(D_PROTO, dev, + "rx: don't know how to deal with proto %02Xh from host %02Xh.\n", + pkthdr->soft.rfc1201.proto, pkthdr->hard.source); } - static int null_build_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, uint8_t daddr) { struct arcnet_local *lp = netdev_priv(dev); - BUGMSG(D_PROTO, - "tx: can't build header for encap %02Xh; load a protocol driver.\n", - lp->default_proto[daddr]); + arc_printk(D_PROTO, dev, + "tx: can't build header for encap %02Xh; load a protocol driver.\n", + lp->default_proto[daddr]); /* always fails */ return 0; } - /* the "do nothing" prepare_tx function warns that there's nothing to do. */ static int null_prepare_tx(struct net_device *dev, struct archdr *pkt, int length, int bufnum) @@ -1033,7 +1000,7 @@ static int null_prepare_tx(struct net_device *dev, struct archdr *pkt, struct arcnet_local *lp = netdev_priv(dev); struct arc_hardware newpkt; - BUGMSG(D_PROTO, "tx: no encap for this host; load a protocol driver.\n"); + arc_printk(D_PROTO, dev, "tx: no encap for this host; load a protocol driver.\n"); /* send a packet to myself -- will never get received, of course */ newpkt.source = newpkt.dest = dev->dev_addr[0]; diff --git a/drivers/net/arcnet/capmode.c b/drivers/net/arcnet/capmode.c index 42fce91b71fc..2056878fb087 100644 --- a/drivers/net/arcnet/capmode.c +++ b/drivers/net/arcnet/capmode.c @@ -26,6 +26,8 @@ * ********************** */ +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/gfp.h> #include <linux/init.h> @@ -33,9 +35,8 @@ #include <net/arp.h> #include <linux/netdevice.h> #include <linux/skbuff.h> -#include <linux/arcdevice.h> -#define VERSION "arcnet: cap mode (`c') encapsulation support loaded.\n" +#include "arcdevice.h" /* packet receiver */ static void rx(struct net_device *dev, int bufnum, @@ -47,7 +48,8 @@ static void rx(struct net_device *dev, int bufnum, char *pktbuf, *pkthdrbuf; int ofs; - BUGMSG(D_DURING, "it's a raw(cap) packet (length=%d)\n", length); + arc_printk(D_DURING, dev, "it's a raw(cap) packet (length=%d)\n", + length); if (length >= MinTU) ofs = 512 - length; @@ -55,8 +57,7 @@ static void rx(struct net_device *dev, int bufnum, ofs = 256 - length; skb = alloc_skb(length + ARC_HDR_SIZE + sizeof(int), GFP_ATOMIC); - if (skb == NULL) { - BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n"); + if (!skb) { dev->stats.rx_dropped++; return; } @@ -66,17 +67,17 @@ static void rx(struct net_device *dev, int bufnum, pkt = (struct archdr *)skb_mac_header(skb); skb_pull(skb, ARC_HDR_SIZE); - /* up to sizeof(pkt->soft) has already been copied from the card */ - /* squeeze in an int for the cap encapsulation */ - - /* use these variables to be sure we count in bytes, not in - sizeof(struct archdr) */ - pktbuf=(char*)pkt; - pkthdrbuf=(char*)pkthdr; - memcpy(pktbuf, pkthdrbuf, ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto)); - memcpy(pktbuf+ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto)+sizeof(int), - pkthdrbuf+ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto), - sizeof(struct archdr)-ARC_HDR_SIZE-sizeof(pkt->soft.cap.proto)); + /* up to sizeof(pkt->soft) has already been copied from the card + * squeeze in an int for the cap encapsulation + * use these variables to be sure we count in bytes, not in + * sizeof(struct archdr) + */ + pktbuf = (char *)pkt; + pkthdrbuf = (char *)pkthdr; + memcpy(pktbuf, pkthdrbuf, ARC_HDR_SIZE + sizeof(pkt->soft.cap.proto)); + memcpy(pktbuf + ARC_HDR_SIZE + sizeof(pkt->soft.cap.proto) + sizeof(int), + pkthdrbuf + ARC_HDR_SIZE + sizeof(pkt->soft.cap.proto), + sizeof(struct archdr) - ARC_HDR_SIZE - sizeof(pkt->soft.cap.proto)); if (length > sizeof(pkt->soft)) lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft), @@ -84,15 +85,14 @@ static void rx(struct net_device *dev, int bufnum, + sizeof(int), length - sizeof(pkt->soft)); - BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx"); + if (BUGLVL(D_SKB)) + arcnet_dump_skb(dev, skb, "rx"); skb->protocol = cpu_to_be16(ETH_P_ARCNET); netif_rx(skb); } - -/* - * Create the ARCnet hard/soft headers for cap mode. +/* Create the ARCnet hard/soft headers for cap mode. * There aren't any soft headers in cap mode - not even the protocol id. */ static int build_header(struct sk_buff *skb, @@ -101,12 +101,12 @@ static int build_header(struct sk_buff *skb, uint8_t daddr) { int hdr_size = ARC_HDR_SIZE; - struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size); + struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size); - BUGMSG(D_PROTO, "Preparing header for cap packet %x.\n", - *((int*)&pkt->soft.cap.cookie[0])); - /* - * Set the source hardware address. + arc_printk(D_PROTO, dev, "Preparing header for cap packet %x.\n", + *((int *)&pkt->soft.cap.cookie[0])); + + /* Set the source hardware address. * * This is pretty pointless for most purposes, but it can help in * debugging. ARCnet does not allow us to change the source address in @@ -117,9 +117,8 @@ static int build_header(struct sk_buff *skb, /* see linux/net/ethernet/eth.c to see where I got the following */ if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { - /* - * FIXME: fill in the last byte of the dest ipaddr here to better - * comply with RFC1051 in "noarp" mode. + /* FIXME: fill in the last byte of the dest ipaddr here to + * better comply with RFC1051 in "noarp" mode. */ pkt->hard.dest = 0; return hdr_size; @@ -130,7 +129,6 @@ static int build_header(struct sk_buff *skb, return hdr_size; /* success */ } - static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, int bufnum) { @@ -138,22 +136,21 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, struct arc_hardware *hard = &pkt->hard; int ofs; - /* hard header is not included in packet length */ length -= ARC_HDR_SIZE; /* And neither is the cookie field */ length -= sizeof(int); - BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n", - lp->next_tx, lp->cur_tx, bufnum); + arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n", + lp->next_tx, lp->cur_tx, bufnum); - BUGMSG(D_PROTO, "Sending for cap packet %x.\n", - *((int*)&pkt->soft.cap.cookie[0])); + arc_printk(D_PROTO, dev, "Sending for cap packet %x.\n", + *((int *)&pkt->soft.cap.cookie[0])); if (length > XMTU) { /* should never happen! other people already check for this. */ - BUGMSG(D_NORMAL, "Bug! prepare_tx with size %d (> %d)\n", - length, XMTU); + arc_printk(D_NORMAL, dev, "Bug! prepare_tx with size %d (> %d)\n", + length, XMTU); length = XMTU; } if (length > MinTU) { @@ -162,11 +159,12 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, } else if (length > MTU) { hard->offset[0] = 0; hard->offset[1] = ofs = 512 - length - 3; - } else + } else { hard->offset[0] = ofs = 256 - length; + } - BUGMSG(D_DURING, "prepare_tx: length=%d ofs=%d\n", - length,ofs); + arc_printk(D_DURING, dev, "prepare_tx: length=%d ofs=%d\n", + length, ofs); /* Copy the arcnet-header + the protocol byte down: */ lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE); @@ -174,9 +172,10 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, sizeof(pkt->soft.cap.proto)); /* Skip the extra integer we have written into it as a cookie - but write the rest of the message: */ - lp->hw.copy_to_card(dev, bufnum, ofs+1, - ((unsigned char*)&pkt->soft.cap.mes),length-1); + * but write the rest of the message: + */ + lp->hw.copy_to_card(dev, bufnum, ofs + 1, + ((unsigned char *)&pkt->soft.cap.mes), length - 1); lp->lastload_dest = hard->dest; @@ -188,21 +187,20 @@ static int ack_tx(struct net_device *dev, int acked) struct arcnet_local *lp = netdev_priv(dev); struct sk_buff *ackskb; struct archdr *ackpkt; - int length=sizeof(struct arc_cap); + int length = sizeof(struct arc_cap); - BUGMSG(D_DURING, "capmode: ack_tx: protocol: %x: result: %d\n", - lp->outgoing.skb->protocol, acked); + arc_printk(D_DURING, dev, "capmode: ack_tx: protocol: %x: result: %d\n", + lp->outgoing.skb->protocol, acked); - BUGLVL(D_SKB) arcnet_dump_skb(dev, lp->outgoing.skb, "ack_tx"); + if (BUGLVL(D_SKB)) + arcnet_dump_skb(dev, lp->outgoing.skb, "ack_tx"); /* Now alloc a skb to send back up through the layers: */ - ackskb = alloc_skb(length + ARC_HDR_SIZE , GFP_ATOMIC); - if (ackskb == NULL) { - BUGMSG(D_NORMAL, "Memory squeeze, can't acknowledge.\n"); + ackskb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC); + if (!ackskb) goto free_outskb; - } - skb_put(ackskb, length + ARC_HDR_SIZE ); + skb_put(ackskb, length + ARC_HDR_SIZE); ackskb->dev = dev; skb_reset_mac_header(ackskb); @@ -212,39 +210,40 @@ static int ack_tx(struct net_device *dev, int acked) skb_copy_from_linear_data(lp->outgoing.skb, ackpkt, ARC_HDR_SIZE + sizeof(struct arc_cap)); ackpkt->soft.cap.proto = 0; /* using protocol 0 for acknowledge */ - ackpkt->soft.cap.mes.ack=acked; + ackpkt->soft.cap.mes.ack = acked; - BUGMSG(D_PROTO, "Ackknowledge for cap packet %x.\n", - *((int*)&ackpkt->soft.cap.cookie[0])); + arc_printk(D_PROTO, dev, "Ackknowledge for cap packet %x.\n", + *((int *)&ackpkt->soft.cap.cookie[0])); ackskb->protocol = cpu_to_be16(ETH_P_ARCNET); - BUGLVL(D_SKB) arcnet_dump_skb(dev, ackskb, "ack_tx_recv"); + if (BUGLVL(D_SKB)) + arcnet_dump_skb(dev, ackskb, "ack_tx_recv"); netif_rx(ackskb); free_outskb: dev_kfree_skb_irq(lp->outgoing.skb); - lp->outgoing.proto = NULL; /* We are always finished when in this protocol */ + lp->outgoing.proto = NULL; + /* We are always finished when in this protocol */ return 0; } -static struct ArcProto capmode_proto = -{ - 'r', - XMTU, - 0, - rx, - build_header, - prepare_tx, - NULL, - ack_tx +static struct ArcProto capmode_proto = { + .suffix = 'r', + .mtu = XMTU, + .rx = rx, + .build_header = build_header, + .prepare_tx = prepare_tx, + .ack_tx = ack_tx }; -static void arcnet_cap_init(void) +static int __init capmode_module_init(void) { int count; + pr_info("cap mode (`c') encapsulation support loaded\n"); + for (count = 1; count <= 8; count++) if (arc_proto_map[count] == arc_proto_default) arc_proto_map[count] = &capmode_proto; @@ -255,12 +254,7 @@ static void arcnet_cap_init(void) arc_proto_default = &capmode_proto; arc_raw_proto = &capmode_proto; -} -static int __init capmode_module_init(void) -{ - printk(VERSION); - arcnet_cap_init(); return 0; } diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c index 45c61a2c5fbd..b9e9931353b2 100644 --- a/drivers/net/arcnet/com20020-isa.c +++ b/drivers/net/arcnet/com20020-isa.c @@ -1,6 +1,6 @@ /* * Linux ARCnet driver - COM20020 chipset support - * + * * Written 1997 by David Woodhouse. * Written 1994-1999 by Avery Pennarun. * Written 1999-2000 by Martin Mares <mj@ucw.cz>. @@ -25,6 +25,9 @@ * * ********************** */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -36,16 +39,12 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/bootmem.h> -#include <linux/arcdevice.h> -#include <linux/com20020.h> - -#include <asm/io.h> +#include <linux/io.h> -#define VERSION "arcnet: COM20020 ISA support (by David Woodhouse et al.)\n" +#include "arcdevice.h" +#include "com20020.h" - -/* - * We cannot (yet) probe for an IO mapped card, although we can check that +/* We cannot (yet) probe for an IO mapped card, although we can check that * it's where we were told it was, and even do autoirq. */ static int __init com20020isa_probe(struct net_device *dev) @@ -55,21 +54,21 @@ static int __init com20020isa_probe(struct net_device *dev) struct arcnet_local *lp = netdev_priv(dev); int err; - BUGLVL(D_NORMAL) printk(VERSION); + if (BUGLVL(D_NORMAL)) + pr_info("%s\n", "COM20020 ISA support (by David Woodhouse et al.)"); ioaddr = dev->base_addr; if (!ioaddr) { - BUGMSG(D_NORMAL, "No autoprobe (yet) for IO mapped cards; you " - "must specify the base address!\n"); + arc_printk(D_NORMAL, dev, "No autoprobe (yet) for IO mapped cards; you must specify the base address!\n"); return -ENODEV; } if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "arcnet (COM20020)")) { - BUGMSG(D_NORMAL, "IO region %xh-%xh already allocated.\n", - ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1); + arc_printk(D_NORMAL, dev, "IO region %xh-%xh already allocated.\n", + ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1); return -ENXIO; } - if (ASTATUS() == 0xFF) { - BUGMSG(D_NORMAL, "IO address %x empty\n", ioaddr); + if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) { + arc_printk(D_NORMAL, dev, "IO address %x empty\n", ioaddr); err = -ENODEV; goto out; } @@ -83,23 +82,24 @@ static int __init com20020isa_probe(struct net_device *dev) * card has just reset and the NORXflag is on until * we tell it to start receiving. */ - BUGMSG(D_INIT_REASONS, "intmask was %02Xh\n", inb(_INTMASK)); - outb(0, _INTMASK); + arc_printk(D_INIT_REASONS, dev, "intmask was %02Xh\n", + arcnet_inb(ioaddr, COM20020_REG_R_STATUS)); + arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK); airqmask = probe_irq_on(); - outb(NORXflag, _INTMASK); + arcnet_outb(NORXflag, ioaddr, COM20020_REG_W_INTMASK); udelay(1); - outb(0, _INTMASK); + arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK); dev->irq = probe_irq_off(airqmask); if ((int)dev->irq <= 0) { - BUGMSG(D_INIT_REASONS, "Autoprobe IRQ failed first time\n"); + arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed first time\n"); airqmask = probe_irq_on(); - outb(NORXflag, _INTMASK); + arcnet_outb(NORXflag, ioaddr, COM20020_REG_W_INTMASK); udelay(5); - outb(0, _INTMASK); + arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK); dev->irq = probe_irq_off(airqmask); if ((int)dev->irq <= 0) { - BUGMSG(D_NORMAL, "Autoprobe IRQ failed.\n"); + arc_printk(D_NORMAL, dev, "Autoprobe IRQ failed.\n"); err = -ENODEV; goto out; } @@ -107,7 +107,9 @@ static int __init com20020isa_probe(struct net_device *dev) } lp->card_name = "ISA COM20020"; - if ((err = com20020_found(dev, 0)) != 0) + + err = com20020_found(dev, 0); + if (err != 0) goto out; return 0; @@ -194,7 +196,7 @@ static int __init com20020isa_setup(char *s) switch (ints[0]) { default: /* ERROR */ - printk("com90xx: Too many arguments.\n"); + pr_info("Too many arguments\n"); case 6: /* Timeout */ timeout = ints[6]; case 5: /* CKP value */ diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c index 96edc1346124..a12bf83be750 100644 --- a/drivers/net/arcnet/com20020-pci.c +++ b/drivers/net/arcnet/com20020-pci.c @@ -1,7 +1,7 @@ /* * Linux ARCnet driver - COM20020 PCI support * Contemporary Controls PCI20 and SOHARD SH-ARC PCI - * + * * Written 1994-1999 by Avery Pennarun, * based on an ISA version by David Woodhouse. * Written 1999-2000 by Martin Mares <mj@ucw.cz>. @@ -26,6 +26,9 @@ * * ********************** */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -36,14 +39,11 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> -#include <linux/arcdevice.h> -#include <linux/com20020.h> #include <linux/list.h> +#include <linux/io.h> -#include <asm/io.h> - - -#define VERSION "arcnet: COM20020 PCI support\n" +#include "arcdevice.h" +#include "com20020.h" /* Module parameters */ @@ -64,7 +64,8 @@ MODULE_LICENSE("GPL"); static void com20020pci_remove(struct pci_dev *pdev); -static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +static int com20020pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) { struct com20020_pci_card_info *ci; struct net_device *dev; @@ -86,7 +87,6 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i INIT_LIST_HEAD(&priv->list_dev); - for (i = 0; i < ci->devcount; i++) { struct com20020_pci_channel_map *cm = &ci->chan_map_tbl[i]; struct com20020_dev *card; @@ -101,13 +101,13 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i lp = netdev_priv(dev); - BUGMSG(D_NORMAL, "%s Controls\n", ci->name); + arc_printk(D_NORMAL, dev, "%s Controls\n", ci->name); ioaddr = pci_resource_start(pdev, cm->bar) + cm->offset; r = devm_request_region(&pdev->dev, ioaddr, cm->size, "com20020-pci"); if (!r) { - pr_err("IO region %xh-%xh already allocated.\n", + pr_err("IO region %xh-%xh already allocated\n", ioaddr, ioaddr + cm->size - 1); ret = -EBUSY; goto out_port; @@ -117,8 +117,8 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i * ARCNET controller needs * this access to detect bustype */ - outb(0x00, ioaddr + 1); - inb(ioaddr + 1); + arcnet_outb(0x00, ioaddr, COM20020_REG_W_COMMAND); + arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT); dev->base_addr = ioaddr; dev->dev_addr[0] = node; @@ -131,7 +131,7 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i lp->timeout = timeout; lp->hw.owner = THIS_MODULE; - if (ASTATUS() == 0xFF) { + if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) { pr_err("IO address %Xh is empty!\n", ioaddr); ret = -EIO; goto out_port; @@ -143,10 +143,8 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev), GFP_KERNEL); - if (!card) { - pr_err("%s out of memory!\n", __func__); + if (!card) return -ENOMEM; - } card->index = i; card->pci_priv = priv; @@ -190,7 +188,11 @@ static struct com20020_pci_card_info card_info_10mbit = { .name = "ARC-PCI", .devcount = 1, .chan_map_tbl = { - { 2, 0x00, 0x08 }, + { + .bar = 2, + .offset = 0x00, + .size = 0x08, + }, }, .flags = ARC_CAN_10MBIT, }; @@ -199,7 +201,11 @@ static struct com20020_pci_card_info card_info_5mbit = { .name = "ARC-PCI", .devcount = 1, .chan_map_tbl = { - { 2, 0x00, 0x08 }, + { + .bar = 2, + .offset = 0x00, + .size = 0x08, + }, }, .flags = ARC_IS_5MBIT, }; @@ -209,7 +215,11 @@ static struct com20020_pci_card_info card_info_sohard = { .devcount = 1, /* SOHARD needs PCI base addr 4 */ .chan_map_tbl = { - {4, 0x00, 0x08}, + { + .bar = 4, + .offset = 0x00, + .size = 0x08 + }, }, .flags = ARC_CAN_10MBIT, }; @@ -218,7 +228,11 @@ static struct com20020_pci_card_info card_info_eae_arc1 = { .name = "EAE PLX-PCI ARC1", .devcount = 1, .chan_map_tbl = { - { 2, 0x00, 0x08 }, + { + .bar = 2, + .offset = 0x00, + .size = 0x08, + }, }, .flags = ARC_CAN_10MBIT, }; @@ -227,8 +241,15 @@ static struct com20020_pci_card_info card_info_eae_ma1 = { .name = "EAE PLX-PCI MA1", .devcount = 2, .chan_map_tbl = { - { 2, 0x00, 0x08 }, - { 2, 0x08, 0x08 } + { + .bar = 2, + .offset = 0x00, + .size = 0x08, + }, { + .bar = 2, + .offset = 0x08, + .size = 0x08, + } }, .flags = ARC_CAN_10MBIT, }; @@ -404,7 +425,8 @@ static struct pci_driver com20020pci_driver = { static int __init com20020pci_init(void) { - BUGLVL(D_NORMAL) printk(VERSION); + if (BUGLVL(D_NORMAL)) + pr_info("%s\n", "COM20020 PCI support"); return pci_register_driver(&com20020pci_driver); } diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c index 1a8437842fbc..c82f323a8c2b 100644 --- a/drivers/net/arcnet/com20020.c +++ b/drivers/net/arcnet/com20020.c @@ -1,6 +1,6 @@ /* * Linux ARCnet driver - COM20020 chipset support - * + * * Written 1997 by David Woodhouse. * Written 1994-1999 by Avery Pennarun. * Written 1999 by Martin Mares <mj@ucw.cz>. @@ -25,6 +25,9 @@ * * ********************** */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> @@ -34,17 +37,16 @@ #include <linux/netdevice.h> #include <linux/init.h> #include <linux/interrupt.h> -#include <linux/arcdevice.h> -#include <linux/com20020.h> +#include <linux/io.h> -#include <asm/io.h> +#include "arcdevice.h" +#include "com20020.h" -#define VERSION "arcnet: COM20020 chipset support (by David Woodhouse et al.)\n" - -static char *clockrates[] = -{"10 Mb/s", "Reserved", "5 Mb/s", - "2.5 Mb/s", "1.25Mb/s", "625 Kb/s", "312.5 Kb/s", - "156.25 Kb/s", "Reserved", "Reserved", "Reserved"}; +static const char * const clockrates[] = { + "XXXXXXX", "XXXXXXXX", "XXXXXX", "2.5 Mb/s", + "1.25Mb/s", "625 Kb/s", "312.5 Kb/s", "156.25 Kb/s", + "Reserved", "Reserved", "Reserved" +}; static void com20020_command(struct net_device *dev, int command); static int com20020_status(struct net_device *dev); @@ -63,35 +65,38 @@ static void com20020_copy_from_card(struct net_device *dev, int bufnum, int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset; /* set up the address register */ - outb((ofs >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI); - outb(ofs & 0xff, _ADDR_LO); + arcnet_outb((ofs >> 8) | RDDATAflag | AUTOINCflag, + ioaddr, COM20020_REG_W_ADDR_HI); + arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO); /* copy the data */ - TIME("insb", count, insb(_MEMDATA, buf, count)); + TIME(dev, "insb", count, + arcnet_insb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count)); } - static void com20020_copy_to_card(struct net_device *dev, int bufnum, int offset, void *buf, int count) { int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset; /* set up the address register */ - outb((ofs >> 8) | AUTOINCflag, _ADDR_HI); - outb(ofs & 0xff, _ADDR_LO); + arcnet_outb((ofs >> 8) | AUTOINCflag, ioaddr, COM20020_REG_W_ADDR_HI); + arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO); /* copy the data */ - TIME("outsb", count, outsb(_MEMDATA, buf, count)); + TIME(dev, "outsb", count, + arcnet_outsb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count)); } - /* Reset the card and check some basic stuff during the detection stage. */ int com20020_check(struct net_device *dev) { int ioaddr = dev->base_addr, status; struct arcnet_local *lp = netdev_priv(dev); - ARCRESET0; + arcnet_outb(XTOcfg(3) | RESETcfg, ioaddr, COM20020_REG_W_CONFIG); + udelay(5); + arcnet_outb(XTOcfg(3), ioaddr, COM20020_REG_W_CONFIG); mdelay(RESETtime); lp->setup = lp->clockm ? 0 : (lp->clockp << 1); @@ -101,49 +106,51 @@ int com20020_check(struct net_device *dev) /* Enable P1Mode for backplane mode */ lp->setup = lp->setup | P1MODE; - SET_SUBADR(SUB_SETUP1); - outb(lp->setup, _XREG); + com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); + arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); + + if (lp->clockm != 0) { + com20020_set_subaddress(lp, ioaddr, SUB_SETUP2); + arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG); - if (lp->clockm != 0) - { - SET_SUBADR(SUB_SETUP2); - outb(lp->setup2, _XREG); - /* must now write the magic "restart operation" command */ mdelay(1); - outb(0x18, _COMMAND); + arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND); } - lp->config = 0x21 | (lp->timeout << 3) | (lp->backplane << 2); + lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; /* set node ID to 0x42 (but transmitter is disabled, so it's okay) */ - SETCONF; - outb(0x42, ioaddr + BUS_ALIGN*7); + arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + arcnet_outb(0x42, ioaddr, COM20020_REG_W_XREG); - status = ASTATUS(); + status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS); if ((status & 0x99) != (NORXflag | TXFREEflag | RESETflag)) { - BUGMSG(D_NORMAL, "status invalid (%Xh).\n", status); + arc_printk(D_NORMAL, dev, "status invalid (%Xh).\n", status); return -ENODEV; } - BUGMSG(D_INIT_REASONS, "status after reset: %X\n", status); + arc_printk(D_INIT_REASONS, dev, "status after reset: %X\n", status); /* Enable TX */ - outb(0x39, _CONFIG); - outb(inb(ioaddr + BUS_ALIGN*8), ioaddr + BUS_ALIGN*7); + lp->config |= TXENcfg; + arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + arcnet_outb(arcnet_inb(ioaddr, 8), ioaddr, COM20020_REG_W_XREG); - ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear); - - status = ASTATUS(); - BUGMSG(D_INIT_REASONS, "status after reset acknowledged: %X\n", - status); + arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, + ioaddr, COM20020_REG_W_COMMAND); + status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS); + arc_printk(D_INIT_REASONS, dev, "status after reset acknowledged: %X\n", + status); /* Read first location of memory */ - outb(0 | RDDATAflag | AUTOINCflag, _ADDR_HI); - outb(0, _ADDR_LO); - - if ((status = inb(_MEMDATA)) != TESTvalue) { - BUGMSG(D_NORMAL, "Signature byte not found (%02Xh != D1h).\n", - status); + arcnet_outb(0 | RDDATAflag | AUTOINCflag, + ioaddr, COM20020_REG_W_ADDR_HI); + arcnet_outb(0, ioaddr, COM20020_REG_W_ADDR_LO); + + status = arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA); + if (status != TESTvalue) { + arc_printk(D_NORMAL, dev, "Signature byte not found (%02Xh != D1h).\n", + status); return -ENODEV; } return 0; @@ -156,8 +163,8 @@ static int com20020_set_hwaddr(struct net_device *dev, void *addr) struct sockaddr *hwaddr = addr; memcpy(dev->dev_addr, hwaddr->sa_data, 1); - SET_SUBADR(SUB_NODE); - outb(dev->dev_addr[0], _XREG); + com20020_set_subaddress(lp, ioaddr, SUB_NODE); + arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG); return 0; } @@ -192,48 +199,54 @@ int com20020_found(struct net_device *dev, int shared) lp->hw.copy_from_card = com20020_copy_from_card; lp->hw.close = com20020_close; + /* FIXME: do this some other way! */ if (!dev->dev_addr[0]) - dev->dev_addr[0] = inb(ioaddr + BUS_ALIGN*8); /* FIXME: do this some other way! */ + dev->dev_addr[0] = arcnet_inb(ioaddr, 8); - SET_SUBADR(SUB_SETUP1); - outb(lp->setup, _XREG); + com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); + arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); + + if (lp->card_flags & ARC_CAN_10MBIT) { + com20020_set_subaddress(lp, ioaddr, SUB_SETUP2); + arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG); - if (lp->card_flags & ARC_CAN_10MBIT) - { - SET_SUBADR(SUB_SETUP2); - outb(lp->setup2, _XREG); - /* must now write the magic "restart operation" command */ mdelay(1); - outb(0x18, _COMMAND); + arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND); } - lp->config = 0x20 | (lp->timeout << 3) | (lp->backplane << 2) | 1; + lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; /* Default 0x38 + register: Node ID */ - SETCONF; - outb(dev->dev_addr[0], _XREG); + arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG); /* reserve the irq */ if (request_irq(dev->irq, arcnet_interrupt, shared, "arcnet (COM20020)", dev)) { - BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); + arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq); return -ENODEV; } dev->base_addr = ioaddr; - BUGMSG(D_NORMAL, "%s: station %02Xh found at %03lXh, IRQ %d.\n", - lp->card_name, dev->dev_addr[0], dev->base_addr, dev->irq); + arc_printk(D_NORMAL, dev, "%s: station %02Xh found at %03lXh, IRQ %d.\n", + lp->card_name, dev->dev_addr[0], dev->base_addr, dev->irq); if (lp->backplane) - BUGMSG(D_NORMAL, "Using backplane mode.\n"); + arc_printk(D_NORMAL, dev, "Using backplane mode.\n"); if (lp->timeout != 3) - BUGMSG(D_NORMAL, "Using extended timeout value of %d.\n", lp->timeout); - - BUGMSG(D_NORMAL, "Using CKP %d - data rate %s.\n", - lp->setup >> 1, - clockrates[3 - ((lp->setup2 & 0xF0) >> 4) + ((lp->setup & 0x0F) >> 1)]); + arc_printk(D_NORMAL, dev, "Using extended timeout value of %d\n", + lp->timeout); + + arc_printk(D_NORMAL, dev, "Using CKP %d - data rate %s\n", + lp->setup >> 1, + clockrates[3 - + ((lp->setup2 & 0xF0) >> 4) + + ((lp->setup & 0x0F) >> 1)]); + /* The clockrates array index looks very fragile. + * It seems like it could have negative indexing. + */ if (register_netdev(dev)) { free_irq(dev->irq, dev); @@ -242,10 +255,8 @@ int com20020_found(struct net_device *dev, int shared) return 0; } - -/* - * Do a hardware reset on the card, and set up necessary registers. - * +/* Do a hardware reset on the card, and set up necessary registers. + * * This should be called as little as possible, because it disrupts the * token on the network (causes a RECON) and requires a significant delay. * @@ -257,65 +268,71 @@ static int com20020_reset(struct net_device *dev, int really_reset) u_int ioaddr = dev->base_addr; u_char inbyte; - BUGMSG(D_DEBUG, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n", - __FILE__,__LINE__,__func__,dev,lp,dev->name); - BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", - dev->name, ASTATUS()); + arc_printk(D_DEBUG, dev, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n", + __FILE__, __LINE__, __func__, dev, lp, dev->name); + arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n", + dev->name, arcnet_inb(ioaddr, COM20020_REG_R_STATUS)); - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2); /* power-up defaults */ - SETCONF; - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); if (really_reset) { /* reset the card */ - ARCRESET; - mdelay(RESETtime * 2); /* COM20020 seems to be slower sometimes */ + arcnet_outb(lp->config | RESETcfg, ioaddr, COM20020_REG_W_CONFIG); + udelay(5); + arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + mdelay(RESETtime * 2); + /* COM20020 seems to be slower sometimes */ } /* clear flags & end reset */ - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); - ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, + ioaddr, COM20020_REG_W_COMMAND); /* verify that the ARCnet signature byte is present */ - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); com20020_copy_from_card(dev, 0, 0, &inbyte, 1); - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); if (inbyte != TESTvalue) { - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); - BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n"); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", + __FILE__, __LINE__, __func__); + arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n"); return 1; } /* enable extended (512-byte) packets */ - ACOMMAND(CONFIGcmd | EXTconf); - BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM20020_REG_W_COMMAND); + + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); /* done! return success. */ return 0; } - static void com20020_setmask(struct net_device *dev, int mask) { u_int ioaddr = dev->base_addr; - BUGMSG(D_DURING, "Setting mask to %x at %x\n",mask,ioaddr); - AINTMASK(mask); -} + arc_printk(D_DURING, dev, "Setting mask to %x at %x\n", mask, ioaddr); + arcnet_outb(mask, ioaddr, COM20020_REG_W_INTMASK); +} static void com20020_command(struct net_device *dev, int cmd) { u_int ioaddr = dev->base_addr; - ACOMMAND(cmd); -} + arcnet_outb(cmd, ioaddr, COM20020_REG_W_COMMAND); +} static int com20020_status(struct net_device *dev) { u_int ioaddr = dev->base_addr; - return ASTATUS() + (ADIAGSTATUS()<<8); + return arcnet_inb(ioaddr, COM20020_REG_R_STATUS) + + (arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT) << 8); } static void com20020_close(struct net_device *dev) @@ -325,7 +342,7 @@ static void com20020_close(struct net_device *dev) /* disable transmitter */ lp->config &= ~TXENcfg; - SETCONF; + arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); } /* Set or clear the multicast filter for this adaptor. @@ -340,20 +357,20 @@ static void com20020_set_mc_list(struct net_device *dev) struct arcnet_local *lp = netdev_priv(dev); int ioaddr = dev->base_addr; - if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP)) { /* Enable promiscuous mode */ + if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP)) { + /* Enable promiscuous mode */ if (!(lp->setup & PROMISCset)) - BUGMSG(D_NORMAL, "Setting promiscuous flag...\n"); - SET_SUBADR(SUB_SETUP1); + arc_printk(D_NORMAL, dev, "Setting promiscuous flag...\n"); + com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); lp->setup |= PROMISCset; - outb(lp->setup, _XREG); - } else + arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); + } else { /* Disable promiscuous mode, use normal mode */ - { if ((lp->setup & PROMISCset)) - BUGMSG(D_NORMAL, "Resetting promiscuous flag...\n"); - SET_SUBADR(SUB_SETUP1); + arc_printk(D_NORMAL, dev, "Resetting promiscuous flag...\n"); + com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); lp->setup &= ~PROMISCset; - outb(lp->setup, _XREG); + arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); } } @@ -371,7 +388,8 @@ MODULE_LICENSE("GPL"); static int __init com20020_module_init(void) { - BUGLVL(D_NORMAL) printk(VERSION); + if (BUGLVL(D_NORMAL)) + pr_info("%s\n", "COM20020 chipset support (by David Woodhouse et al.)"); return 0; } diff --git a/drivers/net/arcnet/com20020.h b/drivers/net/arcnet/com20020.h new file mode 100644 index 000000000000..22a460f39fb9 --- /dev/null +++ b/drivers/net/arcnet/com20020.h @@ -0,0 +1,118 @@ +/* + * Linux ARCnet driver - COM20020 chipset support - function declarations + * + * Written 1997 by David Woodhouse. + * Written 1994-1999 by Avery Pennarun. + * Derived from skeleton.c by Donald Becker. + * + * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com) + * for sponsoring the further development of this driver. + * + * ********************** + * + * The original copyright of skeleton.c was as follows: + * + * skeleton.c Written 1993 by Donald Becker. + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency. This software may only be used + * and distributed according to the terms of the GNU General Public License as + * modified by SRC, incorporated herein by reference. + * + * ********************** + * + * For more details, see drivers/net/arcnet.c + * + * ********************** + */ +#ifndef __COM20020_H +#define __COM20020_H + +int com20020_check(struct net_device *dev); +int com20020_found(struct net_device *dev, int shared); +extern const struct net_device_ops com20020_netdev_ops; + +/* The number of low I/O ports used by the card. */ +#define ARCNET_TOTAL_SIZE 8 + +#define PLX_PCI_MAX_CARDS 2 + +struct com20020_pci_channel_map { + u32 bar; + u32 offset; + u32 size; /* 0x00 - auto, e.g. length of entire bar */ +}; + +struct com20020_pci_card_info { + const char *name; + int devcount; + + struct com20020_pci_channel_map chan_map_tbl[PLX_PCI_MAX_CARDS]; + + unsigned int flags; +}; + +struct com20020_priv { + struct com20020_pci_card_info *ci; + struct list_head list_dev; +}; + +struct com20020_dev { + struct list_head list; + struct net_device *dev; + + struct com20020_priv *pci_priv; + int index; +}; + +#define COM20020_REG_W_INTMASK 0 /* writable */ +#define COM20020_REG_R_STATUS 0 /* readable */ +#define COM20020_REG_W_COMMAND 1 /* standard arcnet commands */ +#define COM20020_REG_R_DIAGSTAT 1 /* diagnostic status */ +#define COM20020_REG_W_ADDR_HI 2 /* control for IO-mapped memory */ +#define COM20020_REG_W_ADDR_LO 3 +#define COM20020_REG_RW_MEMDATA 4 /* data port for IO-mapped memory */ +#define COM20020_REG_W_SUBADR 5 /* the extended port _XREG refers to */ +#define COM20020_REG_W_CONFIG 6 /* configuration */ +#define COM20020_REG_W_XREG 7 /* extra + * (indexed by _CONFIG or _SUBADDR) + */ + +/* in the ADDR_HI register */ +#define RDDATAflag 0x80 /* next access is a read (not a write) */ + +/* in the DIAGSTAT register */ +#define NEWNXTIDflag 0x02 /* ID to which token is passed has changed */ + +/* in the CONFIG register */ +#define RESETcfg 0x80 /* put card in reset state */ +#define TXENcfg 0x20 /* enable TX */ +#define XTOcfg(x) ((x) << 3) /* extended timeout */ + +/* in SETUP register */ +#define PROMISCset 0x10 /* enable RCV_ALL */ +#define P1MODE 0x80 /* enable P1-MODE for Backplane */ +#define SLOWARB 0x01 /* enable Slow Arbitration for >=5Mbps */ + +/* COM2002x */ +#define SUB_TENTATIVE 0 /* tentative node ID */ +#define SUB_NODE 1 /* node ID */ +#define SUB_SETUP1 2 /* various options */ +#define SUB_TEST 3 /* test/diag register */ + +/* COM20022 only */ +#define SUB_SETUP2 4 /* sundry options */ +#define SUB_BUSCTL 5 /* bus control options */ +#define SUB_DMACOUNT 6 /* DMA count options */ + +static inline void com20020_set_subaddress(struct arcnet_local *lp, + int ioaddr, int val) +{ + if (val < 4) { + lp->config = (lp->config & ~0x03) | val; + arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + } else { + arcnet_outb(val, ioaddr, COM20020_REG_W_SUBADR); + } +} + +#endif /* __COM20020_H */ diff --git a/drivers/net/arcnet/com20020_cs.c b/drivers/net/arcnet/com20020_cs.c index 057d9582132a..cf607ffcf358 100644 --- a/drivers/net/arcnet/com20020_cs.c +++ b/drivers/net/arcnet/com20020_cs.c @@ -1,6 +1,6 @@ /* * Linux ARCnet driver - COM20020 PCMCIA support - * + * * Written 1994-1999 by Avery Pennarun, * based on an ISA version by David Woodhouse. * Derived from ibmtr_cs.c by Steve Kipisz (pcmcia-cs 3.1.4) @@ -19,18 +19,21 @@ * Director, National Security Agency. This software may only be used * and distributed according to the terms of the GNU General Public License as * modified by SRC, incorporated herein by reference. - * + * * ********************** * Changes: * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000 * - reorganize kmallocs in com20020_attach, checking all for failure * and releasing the previous allocations if one fails * ********************** - * + * * For more details, see drivers/net/arcnet.c * * ********************** */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/ptrace.h> #include <linux/slab.h> @@ -39,51 +42,44 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/netdevice.h> -#include <linux/arcdevice.h> -#include <linux/com20020.h> - +#include <linux/io.h> #include <pcmcia/cistpl.h> #include <pcmcia/ds.h> -#include <asm/io.h> - -#define VERSION "arcnet: COM20020 PCMCIA support loaded.\n" - +#include "arcdevice.h" +#include "com20020.h" static void regdump(struct net_device *dev) { #ifdef DEBUG - int ioaddr = dev->base_addr; - int count; - - netdev_dbg(dev, "register dump:\n"); - for (count = ioaddr; count < ioaddr + 16; count++) - { - if (!(count % 16)) - pr_cont("%04X:", count); - pr_cont(" %02X", inb(count)); - } - pr_cont("\n"); - - netdev_dbg(dev, "buffer0 dump:\n"); + int ioaddr = dev->base_addr; + int count; + + netdev_dbg(dev, "register dump:\n"); + for (count = 0; count < 16; count++) { + if (!(count % 16)) + pr_cont("%04X:", ioaddr + count); + pr_cont(" %02X", arcnet_inb(ioaddr, count)); + } + pr_cont("\n"); + + netdev_dbg(dev, "buffer0 dump:\n"); /* set up the address register */ - count = 0; - outb((count >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI); - outb(count & 0xff, _ADDR_LO); - - for (count = 0; count < 256+32; count++) - { - if (!(count % 16)) - pr_cont("%04X:", count); - - /* copy the data */ - pr_cont(" %02X", inb(_MEMDATA)); - } - pr_cont("\n"); -#endif -} + count = 0; + arcnet_outb((count >> 8) | RDDATAflag | AUTOINCflag, + ioaddr, com20020_REG_W_ADDR_HI); + arcnet_outb(count & 0xff, ioaddr, COM20020_REG_W_ADDR_LO); + for (count = 0; count < 256 + 32; count++) { + if (!(count % 16)) + pr_cont("%04X:", count); + /* copy the data */ + pr_cont(" %02X", arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA)); + } + pr_cont("\n"); +#endif +} /*====================================================================*/ @@ -114,169 +110,161 @@ static void com20020_detach(struct pcmcia_device *p_dev); static int com20020_probe(struct pcmcia_device *p_dev) { - struct com20020_dev *info; - struct net_device *dev; - struct arcnet_local *lp; + struct com20020_dev *info; + struct net_device *dev; + struct arcnet_local *lp; - dev_dbg(&p_dev->dev, "com20020_attach()\n"); + dev_dbg(&p_dev->dev, "com20020_attach()\n"); - /* Create new network device */ - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - goto fail_alloc_info; + /* Create new network device */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + goto fail_alloc_info; - dev = alloc_arcdev(""); - if (!dev) - goto fail_alloc_dev; + dev = alloc_arcdev(""); + if (!dev) + goto fail_alloc_dev; - lp = netdev_priv(dev); - lp->timeout = timeout; - lp->backplane = backplane; - lp->clockp = clockp; - lp->clockm = clockm & 3; - lp->hw.owner = THIS_MODULE; + lp = netdev_priv(dev); + lp->timeout = timeout; + lp->backplane = backplane; + lp->clockp = clockp; + lp->clockm = clockm & 3; + lp->hw.owner = THIS_MODULE; - /* fill in our module parameters as defaults */ - dev->dev_addr[0] = node; + /* fill in our module parameters as defaults */ + dev->dev_addr[0] = node; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[0]->end = 16; - p_dev->config_flags |= CONF_ENABLE_IRQ; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = 16; + p_dev->config_flags |= CONF_ENABLE_IRQ; - info->dev = dev; - p_dev->priv = info; + info->dev = dev; + p_dev->priv = info; - return com20020_config(p_dev); + return com20020_config(p_dev); fail_alloc_dev: - kfree(info); + kfree(info); fail_alloc_info: - return -ENOMEM; + return -ENOMEM; } /* com20020_attach */ static void com20020_detach(struct pcmcia_device *link) { - struct com20020_dev *info = link->priv; - struct net_device *dev = info->dev; + struct com20020_dev *info = link->priv; + struct net_device *dev = info->dev; - dev_dbg(&link->dev, "detach...\n"); + dev_dbg(&link->dev, "detach...\n"); - dev_dbg(&link->dev, "com20020_detach\n"); + dev_dbg(&link->dev, "com20020_detach\n"); - dev_dbg(&link->dev, "unregister...\n"); + dev_dbg(&link->dev, "unregister...\n"); - unregister_netdev(dev); + unregister_netdev(dev); - /* - * this is necessary because we register our IRQ separately - * from card services. - */ - if (dev->irq) - free_irq(dev->irq, dev); + /* this is necessary because we register our IRQ separately + * from card services. + */ + if (dev->irq) + free_irq(dev->irq, dev); - com20020_release(link); + com20020_release(link); - /* Unlink device structure, free bits */ - dev_dbg(&link->dev, "unlinking...\n"); - if (link->priv) - { - dev = info->dev; - if (dev) - { - dev_dbg(&link->dev, "kfree...\n"); - free_netdev(dev); + /* Unlink device structure, free bits */ + dev_dbg(&link->dev, "unlinking...\n"); + if (link->priv) { + dev = info->dev; + if (dev) { + dev_dbg(&link->dev, "kfree...\n"); + free_netdev(dev); + } + dev_dbg(&link->dev, "kfree2...\n"); + kfree(info); } - dev_dbg(&link->dev, "kfree2...\n"); - kfree(info); - } } /* com20020_detach */ static int com20020_config(struct pcmcia_device *link) { - struct arcnet_local *lp; - struct com20020_dev *info; - struct net_device *dev; - int i, ret; - int ioaddr; + struct arcnet_local *lp; + struct com20020_dev *info; + struct net_device *dev; + int i, ret; + int ioaddr; + + info = link->priv; + dev = info->dev; + + dev_dbg(&link->dev, "config...\n"); + + dev_dbg(&link->dev, "com20020_config\n"); - info = link->priv; - dev = info->dev; + dev_dbg(&link->dev, "baseport1 is %Xh\n", + (unsigned int)link->resource[0]->start); - dev_dbg(&link->dev, "config...\n"); + i = -ENODEV; + link->io_lines = 16; - dev_dbg(&link->dev, "com20020_config\n"); + if (!link->resource[0]->start) { + for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10) { + link->resource[0]->start = ioaddr; + i = pcmcia_request_io(link); + if (i == 0) + break; + } + } else { + i = pcmcia_request_io(link); + } - dev_dbg(&link->dev, "baseport1 is %Xh\n", - (unsigned int) link->resource[0]->start); + if (i != 0) { + dev_dbg(&link->dev, "requestIO failed totally!\n"); + goto failed; + } - i = -ENODEV; - link->io_lines = 16; + ioaddr = dev->base_addr = link->resource[0]->start; + dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr); - if (!link->resource[0]->start) - { - for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10) - { - link->resource[0]->start = ioaddr; - i = pcmcia_request_io(link); - if (i == 0) - break; + dev_dbg(&link->dev, "request IRQ %d\n", + link->irq); + if (!link->irq) { + dev_dbg(&link->dev, "requestIRQ failed totally!\n"); + goto failed; } - } - else - i = pcmcia_request_io(link); - - if (i != 0) - { - dev_dbg(&link->dev, "requestIO failed totally!\n"); - goto failed; - } - - ioaddr = dev->base_addr = link->resource[0]->start; - dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr); - - dev_dbg(&link->dev, "request IRQ %d\n", - link->irq); - if (!link->irq) - { - dev_dbg(&link->dev, "requestIRQ failed totally!\n"); - goto failed; - } - - dev->irq = link->irq; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - if (com20020_check(dev)) - { - regdump(dev); - goto failed; - } - - lp = netdev_priv(dev); - lp->card_name = "PCMCIA COM20020"; - lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */ - - SET_NETDEV_DEV(dev, &link->dev); - - i = com20020_found(dev, 0); /* calls register_netdev */ - - if (i != 0) { - dev_notice(&link->dev, - "com20020_found() failed\n"); - goto failed; - } - - netdev_dbg(dev, "port %#3lx, irq %d\n", - dev->base_addr, dev->irq); - return 0; + + dev->irq = link->irq; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + if (com20020_check(dev)) { + regdump(dev); + goto failed; + } + + lp = netdev_priv(dev); + lp->card_name = "PCMCIA COM20020"; + lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */ + + SET_NETDEV_DEV(dev, &link->dev); + + i = com20020_found(dev, 0); /* calls register_netdev */ + + if (i != 0) { + dev_notice(&link->dev, + "com20020_found() failed\n"); + goto failed; + } + + netdev_dbg(dev, "port %#3lx, irq %d\n", + dev->base_addr, dev->irq); + return 0; failed: - dev_dbg(&link->dev, "com20020_config failed...\n"); - com20020_release(link); - return -ENODEV; + dev_dbg(&link->dev, "com20020_config failed...\n"); + com20020_release(link); + return -ENODEV; } /* com20020_config */ static void com20020_release(struct pcmcia_device *link) @@ -304,7 +292,10 @@ static int com20020_resume(struct pcmcia_device *link) if (link->open) { int ioaddr = dev->base_addr; struct arcnet_local *lp = netdev_priv(dev); - ARCRESET; + + arcnet_outb(lp->config | 0x80, ioaddr, COM20020_REG_W_CONFIG); + udelay(5); + arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); } return 0; @@ -312,9 +303,9 @@ static int com20020_resume(struct pcmcia_device *link) static const struct pcmcia_device_id com20020_ids[] = { PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.", - "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf), + "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf), PCMCIA_DEVICE_PROD_ID12("SoHard AG", - "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7), + "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7), PCMCIA_DEVICE_NULL }; MODULE_DEVICE_TABLE(pcmcia, com20020_ids); diff --git a/drivers/net/arcnet/com9026.h b/drivers/net/arcnet/com9026.h new file mode 100644 index 000000000000..efcaf6707214 --- /dev/null +++ b/drivers/net/arcnet/com9026.h @@ -0,0 +1,17 @@ +#ifndef __COM9026_H +#define __COM9026_H + +/* COM 9026 controller chip --> ARCnet register addresses */ + +#define COM9026_REG_W_INTMASK 0 /* writable */ +#define COM9026_REG_R_STATUS 0 /* readable */ +#define COM9026_REG_W_COMMAND 1 /* writable, returns random vals on read (?) */ +#define COM9026_REG_RW_CONFIG 2 /* Configuration register */ +#define COM9026_REG_R_RESET 8 /* software reset (on read) */ +#define COM9026_REG_RW_MEMDATA 12 /* Data port for IO-mapped memory */ +#define COM9026_REG_W_ADDR_LO 14 /* Control registers for said */ +#define COM9026_REG_W_ADDR_HI 15 + +#define COM9026_REG_R_STATION 1 /* Station ID */ + +#endif diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c index 487d780ebbdf..b57863df5bf5 100644 --- a/drivers/net/arcnet/com90io.c +++ b/drivers/net/arcnet/com90io.c @@ -1,6 +1,6 @@ /* * Linux ARCnet driver - COM90xx chipset (IO-mapped buffers) - * + * * Written 1997 by David Woodhouse. * Written 1994-1999 by Avery Pennarun. * Written 1999-2000 by Martin Mares <mj@ucw.cz>. @@ -25,6 +25,9 @@ * * ********************** */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -34,12 +37,10 @@ #include <linux/bootmem.h> #include <linux/init.h> #include <linux/interrupt.h> -#include <asm/io.h> -#include <linux/arcdevice.h> - - -#define VERSION "arcnet: COM90xx IO-mapped mode support (by David Woodhouse et el.)\n" +#include <linux/io.h> +#include "arcdevice.h" +#include "com9026.h" /* Internal function declarations */ @@ -50,35 +51,14 @@ static void com90io_setmask(struct net_device *dev, int mask); static int com90io_reset(struct net_device *dev, int really_reset); static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset, void *buf, int count); -static void com90io_copy_from_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count); - +static void com90io_copy_from_card(struct net_device *dev, int bufnum, + int offset, void *buf, int count); /* Handy defines for ARCnet specific stuff */ /* The number of low I/O ports used by the card. */ #define ARCNET_TOTAL_SIZE 16 -/* COM 9026 controller chip --> ARCnet register addresses */ -#define _INTMASK (ioaddr+0) /* writable */ -#define _STATUS (ioaddr+0) /* readable */ -#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */ -#define _RESET (ioaddr+8) /* software reset (on read) */ -#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */ -#define _ADDR_HI (ioaddr+15) /* Control registers for said */ -#define _ADDR_LO (ioaddr+14) -#define _CONFIG (ioaddr+2) /* Configuration register */ - -#undef ASTATUS -#undef ACOMMAND -#undef AINTMASK - -#define ASTATUS() inb(_STATUS) -#define ACOMMAND(cmd) outb((cmd),_COMMAND) -#define AINTMASK(msk) outb((msk),_INTMASK) -#define SETCONF() outb((lp->config),_CONFIG) - - /**************************************************************************** * * * IO-mapped operation routines * @@ -92,58 +72,59 @@ static u_char get_buffer_byte(struct net_device *dev, unsigned offset) { int ioaddr = dev->base_addr; - outb(offset >> 8, _ADDR_HI); - outb(offset & 0xff, _ADDR_LO); + arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI); + arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO); - return inb(_MEMDATA); + return arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA); } #ifdef ONE_AT_A_TIME_TX -static void put_buffer_byte(struct net_device *dev, unsigned offset, u_char datum) +static void put_buffer_byte(struct net_device *dev, unsigned offset, + u_char datum) { int ioaddr = dev->base_addr; - outb(offset >> 8, _ADDR_HI); - outb(offset & 0xff, _ADDR_LO); + arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI); + arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO); - outb(datum, _MEMDATA); + arcnet_outb(datum, ioaddr, COM9026_REG_RW_MEMDATA); } #endif - -static void get_whole_buffer(struct net_device *dev, unsigned offset, unsigned length, char *dest) +static void get_whole_buffer(struct net_device *dev, unsigned offset, + unsigned length, char *dest) { int ioaddr = dev->base_addr; - outb((offset >> 8) | AUTOINCflag, _ADDR_HI); - outb(offset & 0xff, _ADDR_LO); + arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI); + arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO); while (length--) #ifdef ONE_AT_A_TIME_RX *(dest++) = get_buffer_byte(dev, offset++); #else - *(dest++) = inb(_MEMDATA); + *(dest++) = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA); #endif } -static void put_whole_buffer(struct net_device *dev, unsigned offset, unsigned length, char *dest) +static void put_whole_buffer(struct net_device *dev, unsigned offset, + unsigned length, char *dest) { int ioaddr = dev->base_addr; - outb((offset >> 8) | AUTOINCflag, _ADDR_HI); - outb(offset & 0xff, _ADDR_LO); + arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI); + arcnet_outb(offset & 0xff, ioaddr,COM9026_REG_W_ADDR_LO); while (length--) #ifdef ONE_AT_A_TIME_TX put_buffer_byte(dev, offset++, *(dest++)); #else - outb(*(dest++), _MEMDATA); + arcnet_outb(*(dest++), ioaddr, COM9026_REG_RW_MEMDATA); #endif } -/* - * We cannot probe for an IO mapped card either, although we can check that +/* We cannot probe for an IO mapped card either, although we can check that * it's where we were told it was, and even autoirq */ static int __init com90io_probe(struct net_device *dev) @@ -151,71 +132,78 @@ static int __init com90io_probe(struct net_device *dev) int ioaddr = dev->base_addr, status; unsigned long airqmask; - BUGLVL(D_NORMAL) printk(VERSION); - BUGLVL(D_NORMAL) printk("E-mail me if you actually test this driver, please!\n"); + if (BUGLVL(D_NORMAL)) { + pr_info("%s\n", "COM90xx IO-mapped mode support (by David Woodhouse et el.)"); + pr_info("E-mail me if you actually test this driver, please!\n"); + } if (!ioaddr) { - BUGMSG(D_NORMAL, "No autoprobe for IO mapped cards; you " - "must specify the base address!\n"); + arc_printk(D_NORMAL, dev, "No autoprobe for IO mapped cards; you must specify the base address!\n"); return -ENODEV; } if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com90io probe")) { - BUGMSG(D_INIT_REASONS, "IO request_region %x-%x failed.\n", - ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1); + arc_printk(D_INIT_REASONS, dev, "IO request_region %x-%x failed\n", + ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1); return -ENXIO; } - if (ASTATUS() == 0xFF) { - BUGMSG(D_INIT_REASONS, "IO address %x empty\n", ioaddr); + if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) { + arc_printk(D_INIT_REASONS, dev, "IO address %x empty\n", + ioaddr); goto err_out; } - inb(_RESET); + arcnet_inb(ioaddr, COM9026_REG_R_RESET); mdelay(RESETtime); - status = ASTATUS(); + status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS); if ((status & 0x9D) != (NORXflag | RECONflag | TXFREEflag | RESETflag)) { - BUGMSG(D_INIT_REASONS, "Status invalid (%Xh).\n", status); + arc_printk(D_INIT_REASONS, dev, "Status invalid (%Xh)\n", + status); goto err_out; } - BUGMSG(D_INIT_REASONS, "Status after reset: %X\n", status); + arc_printk(D_INIT_REASONS, dev, "Status after reset: %X\n", status); - ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear); + arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, + ioaddr, COM9026_REG_W_COMMAND); - BUGMSG(D_INIT_REASONS, "Status after reset acknowledged: %X\n", status); + arc_printk(D_INIT_REASONS, dev, "Status after reset acknowledged: %X\n", + status); - status = ASTATUS(); + status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS); if (status & RESETflag) { - BUGMSG(D_INIT_REASONS, "Eternal reset (status=%Xh)\n", status); + arc_printk(D_INIT_REASONS, dev, "Eternal reset (status=%Xh)\n", + status); goto err_out; } - outb((0x16 | IOMAPflag) & ~ENABLE16flag, _CONFIG); + arcnet_outb((0x16 | IOMAPflag) & ~ENABLE16flag, + ioaddr, COM9026_REG_RW_CONFIG); /* Read first loc'n of memory */ - outb(AUTOINCflag, _ADDR_HI); - outb(0, _ADDR_LO); + arcnet_outb(AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI); + arcnet_outb(0, ioaddr, COM9026_REG_W_ADDR_LO); - if ((status = inb(_MEMDATA)) != 0xd1) { - BUGMSG(D_INIT_REASONS, "Signature byte not found" - " (%Xh instead).\n", status); + status = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA); + if (status != 0xd1) { + arc_printk(D_INIT_REASONS, dev, "Signature byte not found (%Xh instead).\n", + status); goto err_out; } if (!dev->irq) { - /* - * if we do this, we're sure to get an IRQ since the + /* if we do this, we're sure to get an IRQ since the * card has just reset and the NORXflag is on until * we tell it to start receiving. */ airqmask = probe_irq_on(); - outb(NORXflag, _INTMASK); + arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK); udelay(1); - outb(0, _INTMASK); + arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK); dev->irq = probe_irq_off(airqmask); if ((int)dev->irq <= 0) { - BUGMSG(D_INIT_REASONS, "Autoprobe IRQ failed\n"); + arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed\n"); goto err_out; } } @@ -227,7 +215,6 @@ err_out: return -ENODEV; } - /* Set up the struct net_device associated with this card. Called after * probing succeeds. */ @@ -238,12 +225,14 @@ static int __init com90io_found(struct net_device *dev) int err; /* Reserve the irq */ - if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (COM90xx-IO)", dev)) { - BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); + if (request_irq(dev->irq, arcnet_interrupt, 0, + "arcnet (COM90xx-IO)", dev)) { + arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq); return -ENODEV; } /* Reserve the I/O region */ - if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE, "arcnet (COM90xx-IO)")) { + if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE, + "arcnet (COM90xx-IO)")) { free_irq(dev->irq, dev); return -EBUSY; } @@ -259,7 +248,7 @@ static int __init com90io_found(struct net_device *dev) lp->hw.copy_from_card = com90io_copy_from_card; lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag; - SETCONF(); + arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG); /* get and check the station ID from offset 1 in shmem */ @@ -267,21 +256,20 @@ static int __init com90io_found(struct net_device *dev) err = register_netdev(dev); if (err) { - outb((inb(_CONFIG) & ~IOMAPflag), _CONFIG); + arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag, + ioaddr, COM9026_REG_RW_CONFIG); free_irq(dev->irq, dev); release_region(dev->base_addr, ARCNET_TOTAL_SIZE); return err; } - BUGMSG(D_NORMAL, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n", - dev->dev_addr[0], dev->base_addr, dev->irq); + arc_printk(D_NORMAL, dev, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n", + dev->dev_addr[0], dev->base_addr, dev->irq); return 0; } - -/* - * Do a hardware reset on the card, and set up necessary registers. +/* Do a hardware reset on the card, and set up necessary registers. * * This should be called as little as possible, because it disrupts the * token on the network (causes a RECON) and requires a significant delay. @@ -293,67 +281,66 @@ static int com90io_reset(struct net_device *dev, int really_reset) struct arcnet_local *lp = netdev_priv(dev); short ioaddr = dev->base_addr; - BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", dev->name, ASTATUS()); + arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n", + dev->name, arcnet_inb(ioaddr, COM9026_REG_R_STATUS)); if (really_reset) { /* reset the card */ - inb(_RESET); + arcnet_inb(ioaddr, COM9026_REG_R_RESET); mdelay(RESETtime); } /* Set the thing to IO-mapped, 8-bit mode */ lp->config = (0x1C | IOMAPflag) & ~ENABLE16flag; - SETCONF(); + arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG); - ACOMMAND(CFLAGScmd | RESETclear); /* clear flags & end reset */ - ACOMMAND(CFLAGScmd | CONFIGclear); + arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND); + /* clear flags & end reset */ + arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND); /* verify that the ARCnet signature byte is present */ if (get_buffer_byte(dev, 0) != TESTvalue) { - BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n"); + arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n"); return 1; } /* enable extended (512-byte) packets */ - ACOMMAND(CONFIGcmd | EXTconf); - + arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND); /* done! return success. */ return 0; } - static void com90io_command(struct net_device *dev, int cmd) { short ioaddr = dev->base_addr; - ACOMMAND(cmd); + arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND); } - static int com90io_status(struct net_device *dev) { short ioaddr = dev->base_addr; - return ASTATUS(); + return arcnet_inb(ioaddr, COM9026_REG_R_STATUS); } - static void com90io_setmask(struct net_device *dev, int mask) { short ioaddr = dev->base_addr; - AINTMASK(mask); + arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK); } -static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count) +static void com90io_copy_to_card(struct net_device *dev, int bufnum, + int offset, void *buf, int count) { - TIME("put_whole_buffer", count, put_whole_buffer(dev, bufnum * 512 + offset, count, buf)); + TIME(dev, "put_whole_buffer", count, + put_whole_buffer(dev, bufnum * 512 + offset, count, buf)); } - -static void com90io_copy_from_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count) +static void com90io_copy_from_card(struct net_device *dev, int bufnum, + int offset, void *buf, int count) { - TIME("get_whole_buffer", count, get_whole_buffer(dev, bufnum * 512 + offset, count, buf)); + TIME(dev, "get_whole_buffer", count, + get_whole_buffer(dev, bufnum * 512 + offset, count, buf)); } static int io; /* use the insmod io= irq= shmem= options */ @@ -369,12 +356,13 @@ MODULE_LICENSE("GPL"); static int __init com90io_setup(char *s) { int ints[4]; + s = get_options(s, 4, ints); if (!ints[0]) return 0; switch (ints[0]) { default: /* ERROR */ - printk("com90io: Too many arguments.\n"); + pr_err("Too many arguments\n"); case 2: /* IRQ */ irq = ints[2]; case 1: /* IO address */ @@ -421,8 +409,11 @@ static void __exit com90io_exit(void) unregister_netdev(dev); - /* Set the thing back to MMAP mode, in case the old driver is loaded later */ - outb((inb(_CONFIG) & ~IOMAPflag), _CONFIG); + /* In case the old driver is loaded later, + * set the thing back to MMAP mode + */ + arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag, + ioaddr, COM9026_REG_RW_CONFIG); free_irq(dev->irq, dev); release_region(dev->base_addr, ARCNET_TOTAL_SIZE); diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c index b80fbe40aa0e..0d9b45ff1bb2 100644 --- a/drivers/net/arcnet/com90xx.c +++ b/drivers/net/arcnet/com90xx.c @@ -1,6 +1,6 @@ /* * Linux ARCnet driver - COM90xx chipset (memory-mapped buffers) - * + * * Written 1994-1999 by Avery Pennarun. * Written 1999 by Martin Mares <mj@ucw.cz>. * Derived from skeleton.c by Donald Becker. @@ -24,6 +24,9 @@ * * ********************** */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -32,12 +35,10 @@ #include <linux/delay.h> #include <linux/netdevice.h> #include <linux/slab.h> -#include <asm/io.h> -#include <linux/arcdevice.h> - - -#define VERSION "arcnet: COM90xx chipset support\n" +#include <linux/io.h> +#include "arcdevice.h" +#include "com9026.h" /* Define this to speed up the autoprobe by assuming if only one io port and * shmem are left in the list at Stage 5, they must correspond to each @@ -53,7 +54,6 @@ */ #undef FAST_PROBE - /* Internal function declarations */ static int com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem *); static void com90xx_command(struct net_device *dev, int command); @@ -62,8 +62,8 @@ static void com90xx_setmask(struct net_device *dev, int mask); static int com90xx_reset(struct net_device *dev, int really_reset); static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset, void *buf, int count); -static void com90xx_copy_from_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count); +static void com90xx_copy_from_card(struct net_device *dev, int bufnum, + int offset, void *buf, int count); /* Known ARCnet cards */ @@ -77,26 +77,7 @@ static int numcards; /* Amount of I/O memory used by the card */ #define BUFFER_SIZE (512) -#define MIRROR_SIZE (BUFFER_SIZE*4) - -/* COM 9026 controller chip --> ARCnet register addresses */ -#define _INTMASK (ioaddr+0) /* writable */ -#define _STATUS (ioaddr+0) /* readable */ -#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */ -#define _CONFIG (ioaddr+2) /* Configuration register */ -#define _RESET (ioaddr+8) /* software reset (on read) */ -#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */ -#define _ADDR_HI (ioaddr+15) /* Control registers for said */ -#define _ADDR_LO (ioaddr+14) - -#undef ASTATUS -#undef ACOMMAND -#undef AINTMASK - -#define ASTATUS() inb(_STATUS) -#define ACOMMAND(cmd) outb((cmd),_COMMAND) -#define AINTMASK(msk) outb((msk),_INTMASK) - +#define MIRROR_SIZE (BUFFER_SIZE * 4) static int com90xx_skip_probe __initdata = 0; @@ -116,8 +97,7 @@ static void __init com90xx_probe(void) { int count, status, ioaddr, numprint, airq, openparen = 0; unsigned long airqmask; - int ports[(0x3f0 - 0x200) / 16 + 1] = - {0}; + int ports[(0x3f0 - 0x200) / 16 + 1] = { 0 }; unsigned long *shmems; void __iomem **iomem; int numports, numshmems, *port; @@ -127,18 +107,19 @@ static void __init com90xx_probe(void) if (!io && !irq && !shmem && !*device && com90xx_skip_probe) return; - shmems = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(unsigned long), + shmems = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(unsigned long), GFP_KERNEL); if (!shmems) return; - iomem = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(void __iomem *), - GFP_KERNEL); + iomem = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(void __iomem *), + GFP_KERNEL); if (!iomem) { kfree(shmems); return; } - BUGLVL(D_NORMAL) printk(VERSION); + if (BUGLVL(D_NORMAL)) + pr_info("%s\n", "COM90xx chipset support"); /* set up the arrays where we'll store the possible probe addresses */ numports = numshmems = 0; @@ -161,38 +142,43 @@ static void __init com90xx_probe(void) numprint++; numprint %= 8; if (!numprint) { - BUGMSG2(D_INIT, "\n"); - BUGMSG2(D_INIT, "S1: "); + arc_cont(D_INIT, "\n"); + arc_cont(D_INIT, "S1: "); } - BUGMSG2(D_INIT, "%Xh ", *port); + arc_cont(D_INIT, "%Xh ", *port); ioaddr = *port; - if (!request_region(*port, ARCNET_TOTAL_SIZE, "arcnet (90xx)")) { - BUGMSG2(D_INIT_REASONS, "(request_region)\n"); - BUGMSG2(D_INIT_REASONS, "S1: "); - BUGLVL(D_INIT_REASONS) numprint = 0; + if (!request_region(*port, ARCNET_TOTAL_SIZE, + "arcnet (90xx)")) { + arc_cont(D_INIT_REASONS, "(request_region)\n"); + arc_cont(D_INIT_REASONS, "S1: "); + if (BUGLVL(D_INIT_REASONS)) + numprint = 0; *port-- = ports[--numports]; continue; } - if (ASTATUS() == 0xFF) { - BUGMSG2(D_INIT_REASONS, "(empty)\n"); - BUGMSG2(D_INIT_REASONS, "S1: "); - BUGLVL(D_INIT_REASONS) numprint = 0; + if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) { + arc_cont(D_INIT_REASONS, "(empty)\n"); + arc_cont(D_INIT_REASONS, "S1: "); + if (BUGLVL(D_INIT_REASONS)) + numprint = 0; release_region(*port, ARCNET_TOTAL_SIZE); *port-- = ports[--numports]; continue; } - inb(_RESET); /* begin resetting card */ + /* begin resetting card */ + arcnet_inb(ioaddr, COM9026_REG_R_RESET); - BUGMSG2(D_INIT_REASONS, "\n"); - BUGMSG2(D_INIT_REASONS, "S1: "); - BUGLVL(D_INIT_REASONS) numprint = 0; + arc_cont(D_INIT_REASONS, "\n"); + arc_cont(D_INIT_REASONS, "S1: "); + if (BUGLVL(D_INIT_REASONS)) + numprint = 0; } - BUGMSG2(D_INIT, "\n"); + arc_cont(D_INIT, "\n"); if (!numports) { - BUGMSG2(D_NORMAL, "S1: No ARCnet cards found.\n"); + arc_cont(D_NORMAL, "S1: No ARCnet cards found.\n"); kfree(shmems); kfree(iomem); return; @@ -206,12 +192,12 @@ static void __init com90xx_probe(void) numprint++; numprint %= 8; if (!numprint) { - BUGMSG2(D_INIT, "\n"); - BUGMSG2(D_INIT, "S2: "); + arc_cont(D_INIT, "\n"); + arc_cont(D_INIT, "S2: "); } - BUGMSG2(D_INIT, "%Xh ", *port); + arc_cont(D_INIT, "%Xh ", *port); } - BUGMSG2(D_INIT, "\n"); + arc_cont(D_INIT, "\n"); mdelay(RESETtime); /* Stage 3: abandon any shmem addresses that don't have the signature @@ -224,29 +210,33 @@ static void __init com90xx_probe(void) numprint++; numprint %= 8; if (!numprint) { - BUGMSG2(D_INIT, "\n"); - BUGMSG2(D_INIT, "S3: "); + arc_cont(D_INIT, "\n"); + arc_cont(D_INIT, "S3: "); } - BUGMSG2(D_INIT, "%lXh ", *p); + arc_cont(D_INIT, "%lXh ", *p); if (!request_mem_region(*p, MIRROR_SIZE, "arcnet (90xx)")) { - BUGMSG2(D_INIT_REASONS, "(request_mem_region)\n"); - BUGMSG2(D_INIT_REASONS, "Stage 3: "); - BUGLVL(D_INIT_REASONS) numprint = 0; + arc_cont(D_INIT_REASONS, "(request_mem_region)\n"); + arc_cont(D_INIT_REASONS, "Stage 3: "); + if (BUGLVL(D_INIT_REASONS)) + numprint = 0; goto out; } base = ioremap(*p, MIRROR_SIZE); if (!base) { - BUGMSG2(D_INIT_REASONS, "(ioremap)\n"); - BUGMSG2(D_INIT_REASONS, "Stage 3: "); - BUGLVL(D_INIT_REASONS) numprint = 0; + arc_cont(D_INIT_REASONS, "(ioremap)\n"); + arc_cont(D_INIT_REASONS, "Stage 3: "); + if (BUGLVL(D_INIT_REASONS)) + numprint = 0; goto out1; } - if (readb(base) != TESTvalue) { - BUGMSG2(D_INIT_REASONS, "(%02Xh != %02Xh)\n", - readb(base), TESTvalue); - BUGMSG2(D_INIT_REASONS, "S3: "); - BUGLVL(D_INIT_REASONS) numprint = 0; + if (arcnet_readb(base, COM9026_REG_R_STATUS) != TESTvalue) { + arc_cont(D_INIT_REASONS, "(%02Xh != %02Xh)\n", + arcnet_readb(base, COM9026_REG_R_STATUS), + TESTvalue); + arc_cont(D_INIT_REASONS, "S3: "); + if (BUGLVL(D_INIT_REASONS)) + numprint = 0; goto out2; } /* By writing 0x42 to the TESTvalue location, we also make @@ -254,15 +244,16 @@ static void __init com90xx_probe(void) * in another pass through this loop, they will be discarded * because *cptr != TESTvalue. */ - writeb(0x42, base); - if (readb(base) != 0x42) { - BUGMSG2(D_INIT_REASONS, "(read only)\n"); - BUGMSG2(D_INIT_REASONS, "S3: "); + arcnet_writeb(0x42, base, COM9026_REG_W_INTMASK); + if (arcnet_readb(base, COM9026_REG_R_STATUS) != 0x42) { + arc_cont(D_INIT_REASONS, "(read only)\n"); + arc_cont(D_INIT_REASONS, "S3: "); goto out2; } - BUGMSG2(D_INIT_REASONS, "\n"); - BUGMSG2(D_INIT_REASONS, "S3: "); - BUGLVL(D_INIT_REASONS) numprint = 0; + arc_cont(D_INIT_REASONS, "\n"); + arc_cont(D_INIT_REASONS, "S3: "); + if (BUGLVL(D_INIT_REASONS)) + numprint = 0; iomem[index] = base; continue; out2: @@ -273,10 +264,10 @@ static void __init com90xx_probe(void) *p-- = shmems[--numshmems]; index--; } - BUGMSG2(D_INIT, "\n"); + arc_cont(D_INIT, "\n"); if (!numshmems) { - BUGMSG2(D_NORMAL, "S3: No ARCnet cards found.\n"); + arc_cont(D_NORMAL, "S3: No ARCnet cards found.\n"); for (port = &ports[0]; port < ports + numports; port++) release_region(*port, ARCNET_TOTAL_SIZE); kfree(shmems); @@ -291,12 +282,12 @@ static void __init com90xx_probe(void) numprint++; numprint %= 8; if (!numprint) { - BUGMSG2(D_INIT, "\n"); - BUGMSG2(D_INIT, "S4: "); + arc_cont(D_INIT, "\n"); + arc_cont(D_INIT, "S4: "); } - BUGMSG2(D_INIT, "%lXh ", *p); + arc_cont(D_INIT, "%lXh ", *p); } - BUGMSG2(D_INIT, "\n"); + arc_cont(D_INIT, "\n"); /* Stage 5: for any ports that have the correct status, can disable * the RESET flag, and (if no irq is given) generate an autoirq, @@ -308,33 +299,37 @@ static void __init com90xx_probe(void) numprint = -1; for (port = &ports[0]; port < ports + numports; port++) { int found = 0; + numprint++; numprint %= 8; if (!numprint) { - BUGMSG2(D_INIT, "\n"); - BUGMSG2(D_INIT, "S5: "); + arc_cont(D_INIT, "\n"); + arc_cont(D_INIT, "S5: "); } - BUGMSG2(D_INIT, "%Xh ", *port); + arc_cont(D_INIT, "%Xh ", *port); ioaddr = *port; - status = ASTATUS(); + status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS); if ((status & 0x9D) != (NORXflag | RECONflag | TXFREEflag | RESETflag)) { - BUGMSG2(D_INIT_REASONS, "(status=%Xh)\n", status); - BUGMSG2(D_INIT_REASONS, "S5: "); - BUGLVL(D_INIT_REASONS) numprint = 0; + arc_cont(D_INIT_REASONS, "(status=%Xh)\n", status); + arc_cont(D_INIT_REASONS, "S5: "); + if (BUGLVL(D_INIT_REASONS)) + numprint = 0; release_region(*port, ARCNET_TOTAL_SIZE); *port-- = ports[--numports]; continue; } - ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear); - status = ASTATUS(); + arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, + ioaddr, COM9026_REG_W_COMMAND); + status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS); if (status & RESETflag) { - BUGMSG2(D_INIT_REASONS, " (eternal reset, status=%Xh)\n", - status); - BUGMSG2(D_INIT_REASONS, "S5: "); - BUGLVL(D_INIT_REASONS) numprint = 0; + arc_cont(D_INIT_REASONS, " (eternal reset, status=%Xh)\n", + status); + arc_cont(D_INIT_REASONS, "S5: "); + if (BUGLVL(D_INIT_REASONS)) + numprint = 0; release_region(*port, ARCNET_TOTAL_SIZE); *port-- = ports[--numports]; continue; @@ -348,15 +343,16 @@ static void __init com90xx_probe(void) * we tell it to start receiving. */ airqmask = probe_irq_on(); - AINTMASK(NORXflag); + arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK); udelay(1); - AINTMASK(0); + arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK); airq = probe_irq_off(airqmask); if (airq <= 0) { - BUGMSG2(D_INIT_REASONS, "(airq=%d)\n", airq); - BUGMSG2(D_INIT_REASONS, "S5: "); - BUGLVL(D_INIT_REASONS) numprint = 0; + arc_cont(D_INIT_REASONS, "(airq=%d)\n", airq); + arc_cont(D_INIT_REASONS, "S5: "); + if (BUGLVL(D_INIT_REASONS)) + numprint = 0; release_region(*port, ARCNET_TOTAL_SIZE); *port-- = ports[--numports]; continue; @@ -365,7 +361,7 @@ static void __init com90xx_probe(void) airq = irq; } - BUGMSG2(D_INIT, "(%d,", airq); + arc_cont(D_INIT, "(%d,", airq); openparen = 1; /* Everything seems okay. But which shmem, if any, puts @@ -376,14 +372,15 @@ static void __init com90xx_probe(void) */ #ifdef FAST_PROBE if (numports > 1 || numshmems > 1) { - inb(_RESET); + arcnet_inb(ioaddr, COM9026_REG_R_RESET); mdelay(RESETtime); } else { /* just one shmem and port, assume they match */ - writeb(TESTvalue, iomem[0]); + arcnet_writeb(TESTvalue, iomem[0], + COM9026_REG_W_INTMASK); } #else - inb(_RESET); + arcnet_inb(ioaddr, COM9026_REG_R_RESET); mdelay(RESETtime); #endif @@ -391,8 +388,8 @@ static void __init com90xx_probe(void) u_long ptr = shmems[index]; void __iomem *base = iomem[index]; - if (readb(base) == TESTvalue) { /* found one */ - BUGMSG2(D_INIT, "%lXh)\n", *p); + if (arcnet_readb(base, COM9026_REG_R_STATUS) == TESTvalue) { /* found one */ + arc_cont(D_INIT, "%lXh)\n", *p); openparen = 0; /* register the card */ @@ -405,25 +402,30 @@ static void __init com90xx_probe(void) iomem[index] = iomem[numshmems]; break; /* go to the next I/O port */ } else { - BUGMSG2(D_INIT_REASONS, "%Xh-", readb(base)); + arc_cont(D_INIT_REASONS, "%Xh-", + arcnet_readb(base, COM9026_REG_R_STATUS)); } } if (openparen) { - BUGLVL(D_INIT) printk("no matching shmem)\n"); - BUGLVL(D_INIT_REASONS) printk("S5: "); - BUGLVL(D_INIT_REASONS) numprint = 0; + if (BUGLVL(D_INIT)) + pr_cont("no matching shmem)\n"); + if (BUGLVL(D_INIT_REASONS)) { + pr_cont("S5: "); + numprint = 0; + } } if (!found) release_region(*port, ARCNET_TOTAL_SIZE); *port-- = ports[--numports]; } - BUGLVL(D_INIT_REASONS) printk("\n"); + if (BUGLVL(D_INIT_REASONS)) + pr_cont("\n"); /* Now put back TESTvalue on all leftover shmems. */ for (index = 0; index < numshmems; index++) { - writeb(TESTvalue, iomem[index]); + arcnet_writeb(TESTvalue, iomem[index], COM9026_REG_W_INTMASK); iounmap(iomem[index]); release_mem_region(shmems[index], MIRROR_SIZE); } @@ -441,7 +443,7 @@ static int check_mirror(unsigned long addr, size_t size) p = ioremap(addr, size); if (p) { - if (readb(p) == TESTvalue) + if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue) res = 1; else res = 0; @@ -455,7 +457,8 @@ static int check_mirror(unsigned long addr, size_t size) /* Set up the struct net_device associated with this card. Called after * probing succeeds. */ -static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem *p) +static int __init com90xx_found(int ioaddr, int airq, u_long shmem, + void __iomem *p) { struct net_device *dev = NULL; struct arcnet_local *lp; @@ -465,7 +468,7 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem /* allocate struct net_device */ dev = alloc_arcdev(device); if (!dev) { - BUGMSG2(D_NORMAL, "com90xx: Can't allocate device!\n"); + arc_cont(D_NORMAL, "com90xx: Can't allocate device!\n"); iounmap(p); release_mem_region(shmem, MIRROR_SIZE); return -ENOMEM; @@ -478,7 +481,7 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem * 2k (or there are no mirrors at all) but on some, it's 4k. */ mirror_size = MIRROR_SIZE; - if (readb(p) == TESTvalue && + if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue && check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 && check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1) mirror_size = 2 * MIRROR_SIZE; @@ -499,12 +502,14 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem iounmap(p); release_mem_region(shmem, MIRROR_SIZE); - if (!request_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1, "arcnet (90xx)")) + if (!request_mem_region(dev->mem_start, + dev->mem_end - dev->mem_start + 1, + "arcnet (90xx)")) goto err_free_dev; /* reserve the irq */ if (request_irq(airq, arcnet_interrupt, 0, "arcnet (90xx)", dev)) { - BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", airq); + arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", airq); goto err_release_mem; } dev->irq = airq; @@ -518,22 +523,23 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem lp->hw.owner = THIS_MODULE; lp->hw.copy_to_card = com90xx_copy_to_card; lp->hw.copy_from_card = com90xx_copy_from_card; - lp->mem_start = ioremap(dev->mem_start, dev->mem_end - dev->mem_start + 1); + lp->mem_start = ioremap(dev->mem_start, + dev->mem_end - dev->mem_start + 1); if (!lp->mem_start) { - BUGMSG(D_NORMAL, "Can't remap device memory!\n"); + arc_printk(D_NORMAL, dev, "Can't remap device memory!\n"); goto err_free_irq; } /* get and check the station ID from offset 1 in shmem */ - dev->dev_addr[0] = readb(lp->mem_start + 1); + dev->dev_addr[0] = arcnet_readb(lp->mem_start, COM9026_REG_R_STATION); dev->base_addr = ioaddr; - BUGMSG(D_NORMAL, "COM90xx station %02Xh found at %03lXh, IRQ %d, " - "ShMem %lXh (%ld*%xh).\n", - dev->dev_addr[0], - dev->base_addr, dev->irq, dev->mem_start, - (dev->mem_end - dev->mem_start + 1) / mirror_size, mirror_size); + arc_printk(D_NORMAL, dev, "COM90xx station %02Xh found at %03lXh, IRQ %d, ShMem %lXh (%ld*%xh).\n", + dev->dev_addr[0], + dev->base_addr, dev->irq, dev->mem_start, + (dev->mem_end - dev->mem_start + 1) / mirror_size, + mirror_size); if (register_netdev(dev)) goto err_unmap; @@ -552,34 +558,29 @@ err_free_dev: return -EIO; } - static void com90xx_command(struct net_device *dev, int cmd) { short ioaddr = dev->base_addr; - ACOMMAND(cmd); + arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND); } - static int com90xx_status(struct net_device *dev) { short ioaddr = dev->base_addr; - return ASTATUS(); + return arcnet_inb(ioaddr, COM9026_REG_R_STATUS); } - static void com90xx_setmask(struct net_device *dev, int mask) { short ioaddr = dev->base_addr; - AINTMASK(mask); + arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK); } - -/* - * Do a hardware reset on the card, and set up necessary registers. - * +/* Do a hardware reset on the card, and set up necessary registers. + * * This should be called as little as possible, because it disrupts the * token on the network (causes a RECON) and requires a significant delay. * @@ -590,53 +591,58 @@ static int com90xx_reset(struct net_device *dev, int really_reset) struct arcnet_local *lp = netdev_priv(dev); short ioaddr = dev->base_addr; - BUGMSG(D_INIT, "Resetting (status=%02Xh)\n", ASTATUS()); + arc_printk(D_INIT, dev, "Resetting (status=%02Xh)\n", + arcnet_inb(ioaddr, COM9026_REG_R_STATUS)); if (really_reset) { /* reset the card */ - inb(_RESET); + arcnet_inb(ioaddr, COM9026_REG_R_RESET); mdelay(RESETtime); } - ACOMMAND(CFLAGScmd | RESETclear); /* clear flags & end reset */ - ACOMMAND(CFLAGScmd | CONFIGclear); + /* clear flags & end reset */ + arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND); + arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND); +#if 0 /* don't do this until we verify that it doesn't hurt older cards! */ - /* outb(inb(_CONFIG) | ENABLE16flag, _CONFIG); */ + arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) | ENABLE16flag, + ioaddr, COM9026_REG_RW_CONFIG); +#endif /* verify that the ARCnet signature byte is present */ - if (readb(lp->mem_start) != TESTvalue) { + if (arcnet_readb(lp->mem_start, COM9026_REG_R_STATUS) != TESTvalue) { if (really_reset) - BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n"); + arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n"); return 1; } /* enable extended (512-byte) packets */ - ACOMMAND(CONFIGcmd | EXTconf); + arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND); /* clean out all the memory to make debugging make more sense :) */ - BUGLVL(D_DURING) - memset_io(lp->mem_start, 0x42, 2048); + if (BUGLVL(D_DURING)) + memset_io(lp->mem_start, 0x42, 2048); /* done! return success. */ return 0; } -static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count) +static void com90xx_copy_to_card(struct net_device *dev, int bufnum, + int offset, void *buf, int count) { struct arcnet_local *lp = netdev_priv(dev); void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset; - TIME("memcpy_toio", count, memcpy_toio(memaddr, buf, count)); -} + TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count)); +} -static void com90xx_copy_from_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count) +static void com90xx_copy_from_card(struct net_device *dev, int bufnum, + int offset, void *buf, int count) { struct arcnet_local *lp = netdev_priv(dev); void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset; - TIME("memcpy_fromio", count, memcpy_fromio(buf, memaddr, count)); -} + TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count)); +} MODULE_LICENSE("GPL"); @@ -664,7 +670,8 @@ static void __exit com90xx_exit(void) free_irq(dev->irq, dev); iounmap(lp->mem_start); release_region(dev->base_addr, ARCNET_TOTAL_SIZE); - release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1); + release_mem_region(dev->mem_start, + dev->mem_end - dev->mem_start + 1); free_netdev(dev); } } @@ -679,13 +686,13 @@ static int __init com90xx_setup(char *s) s = get_options(s, 8, ints); if (!ints[0] && !*s) { - printk("com90xx: Disabled.\n"); + pr_notice("Disabled\n"); return 1; } switch (ints[0]) { default: /* ERROR */ - printk("com90xx: Too many arguments.\n"); + pr_err("Too many arguments\n"); case 3: /* Mem address */ shmem = ints[3]; case 2: /* IRQ */ diff --git a/drivers/net/arcnet/rfc1051.c b/drivers/net/arcnet/rfc1051.c index f81db4070a57..4b1a75469cb1 100644 --- a/drivers/net/arcnet/rfc1051.c +++ b/drivers/net/arcnet/rfc1051.c @@ -1,6 +1,6 @@ /* * Linux ARCnet driver - RFC1051 ("simple" standard) packet encapsulation - * + * * Written 1994-1999 by Avery Pennarun. * Derived from skeleton.c by Donald Becker. * @@ -23,6 +23,9 @@ * * ********************** */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/gfp.h> #include <linux/init.h> @@ -30,10 +33,8 @@ #include <net/arp.h> #include <linux/netdevice.h> #include <linux/skbuff.h> -#include <linux/arcdevice.h> - -#define VERSION "arcnet: RFC1051 \"simple standard\" (`s') encapsulation support loaded.\n" +#include "arcdevice.h" static __be16 type_trans(struct sk_buff *skb, struct net_device *dev); static void rx(struct net_device *dev, int bufnum, @@ -43,9 +44,7 @@ static int build_header(struct sk_buff *skb, struct net_device *dev, static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, int bufnum); - -static struct ArcProto rfc1051_proto = -{ +static struct ArcProto rfc1051_proto = { .suffix = 's', .mtu = XMTU - RFC1051_HDR_SIZE, .is_ip = 1, @@ -56,10 +55,9 @@ static struct ArcProto rfc1051_proto = .ack_tx = NULL }; - static int __init arcnet_rfc1051_init(void) { - printk(VERSION); + pr_info("%s\n", "RFC1051 \"simple standard\" (`s') encapsulation support loaded"); arc_proto_map[ARC_P_IP_RFC1051] = arc_proto_map[ARC_P_ARP_RFC1051] @@ -82,14 +80,13 @@ module_exit(arcnet_rfc1051_exit); MODULE_LICENSE("GPL"); -/* - * Determine a packet's protocol ID. - * +/* Determine a packet's protocol ID. + * * With ARCnet we have to convert everything to Ethernet-style stuff. */ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev) { - struct archdr *pkt = (struct archdr *) skb->data; + struct archdr *pkt = (struct archdr *)skb->data; struct arc_rfc1051 *soft = &pkt->soft.rfc1051; int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE; @@ -97,9 +94,9 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev) skb_reset_mac_header(skb); skb_pull(skb, hdr_size); - if (pkt->hard.dest == 0) + if (pkt->hard.dest == 0) { skb->pkt_type = PACKET_BROADCAST; - else if (dev->flags & IFF_PROMISC) { + } else if (dev->flags & IFF_PROMISC) { /* if we're not sending to ourselves :) */ if (pkt->hard.dest != dev->dev_addr[0]) skb->pkt_type = PACKET_OTHERHOST; @@ -120,7 +117,6 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev) return htons(ETH_P_IP); } - /* packet receiver */ static void rx(struct net_device *dev, int bufnum, struct archdr *pkthdr, int length) @@ -130,7 +126,7 @@ static void rx(struct net_device *dev, int bufnum, struct archdr *pkt = pkthdr; int ofs; - BUGMSG(D_DURING, "it's a raw packet (length=%d)\n", length); + arc_printk(D_DURING, dev, "it's a raw packet (length=%d)\n", length); if (length >= MinTU) ofs = 512 - length; @@ -138,15 +134,14 @@ static void rx(struct net_device *dev, int bufnum, ofs = 256 - length; skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC); - if (skb == NULL) { - BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n"); + if (!skb) { dev->stats.rx_dropped++; return; } skb_put(skb, length + ARC_HDR_SIZE); skb->dev = dev; - pkt = (struct archdr *) skb->data; + pkt = (struct archdr *)skb->data; /* up to sizeof(pkt->soft) has already been copied from the card */ memcpy(pkt, pkthdr, sizeof(struct archdr)); @@ -155,21 +150,19 @@ static void rx(struct net_device *dev, int bufnum, pkt->soft.raw + sizeof(pkt->soft), length - sizeof(pkt->soft)); - BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx"); + if (BUGLVL(D_SKB)) + arcnet_dump_skb(dev, skb, "rx"); skb->protocol = type_trans(skb, dev); netif_rx(skb); } - -/* - * Create the ARCnet hard/soft headers for RFC1051. - */ +/* Create the ARCnet hard/soft headers for RFC1051 */ static int build_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, uint8_t daddr) { int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE; - struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size); + struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size); struct arc_rfc1051 *soft = &pkt->soft.rfc1051; /* set the protocol ID according to RFC1051 */ @@ -181,29 +174,26 @@ static int build_header(struct sk_buff *skb, struct net_device *dev, soft->proto = ARC_P_ARP_RFC1051; break; default: - BUGMSG(D_NORMAL, "RFC1051: I don't understand protocol %d (%Xh)\n", - type, type); + arc_printk(D_NORMAL, dev, "RFC1051: I don't understand protocol %d (%Xh)\n", + type, type); dev->stats.tx_errors++; dev->stats.tx_aborted_errors++; return 0; } - - /* - * Set the source hardware address. + /* Set the source hardware address. * * This is pretty pointless for most purposes, but it can help in - * debugging. ARCnet does not allow us to change the source address in - * the actual packet sent) + * debugging. ARCnet does not allow us to change the source address + * in the actual packet sent. */ pkt->hard.source = *dev->dev_addr; /* see linux/net/ethernet/eth.c to see where I got the following */ if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { - /* - * FIXME: fill in the last byte of the dest ipaddr here to better - * comply with RFC1051 in "noarp" mode. + /* FIXME: fill in the last byte of the dest ipaddr here to + * better comply with RFC1051 in "noarp" mode. */ pkt->hard.dest = 0; return hdr_size; @@ -214,7 +204,6 @@ static int build_header(struct sk_buff *skb, struct net_device *dev, return hdr_size; /* success */ } - static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, int bufnum) { @@ -222,15 +211,16 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, struct arc_hardware *hard = &pkt->hard; int ofs; - BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n", - lp->next_tx, lp->cur_tx, bufnum); + arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n", + lp->next_tx, lp->cur_tx, bufnum); - length -= ARC_HDR_SIZE; /* hard header is not included in packet length */ + /* hard header is not included in packet length */ + length -= ARC_HDR_SIZE; if (length > XMTU) { /* should never happen! other people already check for this. */ - BUGMSG(D_NORMAL, "Bug! prepare_tx with size %d (> %d)\n", - length, XMTU); + arc_printk(D_NORMAL, dev, "Bug! prepare_tx with size %d (> %d)\n", + length, XMTU); length = XMTU; } if (length > MinTU) { @@ -239,8 +229,9 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, } else if (length > MTU) { hard->offset[0] = 0; hard->offset[1] = ofs = 512 - length - 3; - } else + } else { hard->offset[0] = ofs = 256 - length; + } lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE); lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft, length); diff --git a/drivers/net/arcnet/rfc1201.c b/drivers/net/arcnet/rfc1201.c index b71431aae084..566da5ecdc9d 100644 --- a/drivers/net/arcnet/rfc1201.c +++ b/drivers/net/arcnet/rfc1201.c @@ -1,6 +1,6 @@ /* * Linux ARCnet driver - RFC1201 (standard) packet encapsulation - * + * * Written 1994-1999 by Avery Pennarun. * Derived from skeleton.c by Donald Becker. * @@ -23,17 +23,19 @@ * * ********************** */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + #include <linux/gfp.h> #include <linux/module.h> #include <linux/init.h> #include <linux/if_arp.h> #include <linux/netdevice.h> #include <linux/skbuff.h> -#include <linux/arcdevice.h> -MODULE_LICENSE("GPL"); -#define VERSION "arcnet: RFC1201 \"standard\" (`a') encapsulation support loaded.\n" +#include "arcdevice.h" +MODULE_LICENSE("GPL"); static __be16 type_trans(struct sk_buff *skb, struct net_device *dev); static void rx(struct net_device *dev, int bufnum, @@ -44,8 +46,7 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, int bufnum); static int continue_tx(struct net_device *dev, int bufnum); -static struct ArcProto rfc1201_proto = -{ +static struct ArcProto rfc1201_proto = { .suffix = 'a', .mtu = 1500, /* could be more, but some receivers can't handle it... */ .is_ip = 1, /* This is for sending IP and ARP packages */ @@ -56,10 +57,9 @@ static struct ArcProto rfc1201_proto = .ack_tx = NULL }; - static int __init arcnet_rfc1201_init(void) { - printk(VERSION); + pr_info("%s\n", "RFC1201 \"standard\" (`a') encapsulation support loaded"); arc_proto_map[ARC_P_IP] = arc_proto_map[ARC_P_IPV6] @@ -84,14 +84,13 @@ static void __exit arcnet_rfc1201_exit(void) module_init(arcnet_rfc1201_init); module_exit(arcnet_rfc1201_exit); -/* - * Determine a packet's protocol ID. - * +/* Determine a packet's protocol ID. + * * With ARCnet we have to convert everything to Ethernet-style stuff. */ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev) { - struct archdr *pkt = (struct archdr *) skb->data; + struct archdr *pkt = (struct archdr *)skb->data; struct arc_rfc1201 *soft = &pkt->soft.rfc1201; int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE; @@ -99,9 +98,9 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev) skb_reset_mac_header(skb); skb_pull(skb, hdr_size); - if (pkt->hard.dest == 0) + if (pkt->hard.dest == 0) { skb->pkt_type = PACKET_BROADCAST; - else if (dev->flags & IFF_PROMISC) { + } else if (dev->flags & IFF_PROMISC) { /* if we're not sending to ourselves :) */ if (pkt->hard.dest != dev->dev_addr[0]) skb->pkt_type = PACKET_OTHERHOST; @@ -129,7 +128,6 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev) return htons(ETH_P_IP); } - /* packet receiver */ static void rx(struct net_device *dev, int bufnum, struct archdr *pkthdr, int length) @@ -141,7 +139,8 @@ static void rx(struct net_device *dev, int bufnum, int saddr = pkt->hard.source, ofs; struct Incoming *in = &lp->rfc1201.incoming[saddr]; - BUGMSG(D_DURING, "it's an RFC1201 packet (length=%d)\n", length); + arc_printk(D_DURING, dev, "it's an RFC1201 packet (length=%d)\n", + length); if (length >= MinTU) ofs = 512 - length; @@ -149,11 +148,11 @@ static void rx(struct net_device *dev, int bufnum, ofs = 256 - length; if (soft->split_flag == 0xFF) { /* Exception Packet */ - if (length >= 4 + RFC1201_HDR_SIZE) - BUGMSG(D_DURING, "compensating for exception packet\n"); - else { - BUGMSG(D_EXTRA, "short RFC1201 exception packet from %02Xh", - saddr); + if (length >= 4 + RFC1201_HDR_SIZE) { + arc_printk(D_DURING, dev, "compensating for exception packet\n"); + } else { + arc_printk(D_EXTRA, dev, "short RFC1201 exception packet from %02Xh", + saddr); return; } @@ -164,12 +163,13 @@ static void rx(struct net_device *dev, int bufnum, soft, sizeof(pkt->soft)); } if (!soft->split_flag) { /* not split */ - BUGMSG(D_RX, "incoming is not split (splitflag=%d)\n", - soft->split_flag); + arc_printk(D_RX, dev, "incoming is not split (splitflag=%d)\n", + soft->split_flag); if (in->skb) { /* already assembling one! */ - BUGMSG(D_EXTRA, "aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n", - in->sequence, soft->split_flag, soft->sequence); + arc_printk(D_EXTRA, dev, "aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n", + in->sequence, soft->split_flag, + soft->sequence); lp->rfc1201.aborted_seq = soft->sequence; dev_kfree_skb_irq(in->skb); dev->stats.rx_errors++; @@ -179,82 +179,86 @@ static void rx(struct net_device *dev, int bufnum, in->sequence = soft->sequence; skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC); - if (skb == NULL) { - BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n"); + if (!skb) { dev->stats.rx_dropped++; return; } skb_put(skb, length + ARC_HDR_SIZE); skb->dev = dev; - pkt = (struct archdr *) skb->data; + pkt = (struct archdr *)skb->data; soft = &pkt->soft.rfc1201; - /* up to sizeof(pkt->soft) has already been copied from the card */ + /* up to sizeof(pkt->soft) has already + * been copied from the card + */ memcpy(pkt, pkthdr, sizeof(struct archdr)); if (length > sizeof(pkt->soft)) - lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft), - pkt->soft.raw + sizeof(pkt->soft), + lp->hw.copy_from_card(dev, bufnum, + ofs + sizeof(pkt->soft), + pkt->soft.raw + sizeof(pkt->soft), length - sizeof(pkt->soft)); - /* - * ARP packets have problems when sent from some DOS systems: the - * source address is always 0! So we take the hardware source addr - * (which is impossible to fumble) and insert it ourselves. + /* ARP packets have problems when sent from some DOS systems: + * the source address is always 0! + * So we take the hardware source addr (which is impossible + * to fumble) and insert it ourselves. */ if (soft->proto == ARC_P_ARP) { - struct arphdr *arp = (struct arphdr *) soft->payload; + struct arphdr *arp = (struct arphdr *)soft->payload; /* make sure addresses are the right length */ if (arp->ar_hln == 1 && arp->ar_pln == 4) { - uint8_t *cptr = (uint8_t *) arp + sizeof(struct arphdr); + uint8_t *cptr = (uint8_t *)arp + sizeof(struct arphdr); if (!*cptr) { /* is saddr = 00? */ - BUGMSG(D_EXTRA, - "ARP source address was 00h, set to %02Xh.\n", - saddr); + arc_printk(D_EXTRA, dev, + "ARP source address was 00h, set to %02Xh\n", + saddr); dev->stats.rx_crc_errors++; *cptr = saddr; } else { - BUGMSG(D_DURING, "ARP source address (%Xh) is fine.\n", - *cptr); + arc_printk(D_DURING, dev, "ARP source address (%Xh) is fine.\n", + *cptr); } } else { - BUGMSG(D_NORMAL, "funny-shaped ARP packet. (%Xh, %Xh)\n", - arp->ar_hln, arp->ar_pln); + arc_printk(D_NORMAL, dev, "funny-shaped ARP packet. (%Xh, %Xh)\n", + arp->ar_hln, arp->ar_pln); dev->stats.rx_errors++; dev->stats.rx_crc_errors++; } } - BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx"); + if (BUGLVL(D_SKB)) + arcnet_dump_skb(dev, skb, "rx"); skb->protocol = type_trans(skb, dev); netif_rx(skb); } else { /* split packet */ - /* - * NOTE: MSDOS ARP packet correction should only need to apply to - * unsplit packets, since ARP packets are so short. + /* NOTE: MSDOS ARP packet correction should only need to + * apply to unsplit packets, since ARP packets are so short. * - * My interpretation of the RFC1201 document is that if a packet is - * received out of order, the entire assembly process should be - * aborted. + * My interpretation of the RFC1201 document is that if a + * packet is received out of order, the entire assembly + * process should be aborted. * - * The RFC also mentions "it is possible for successfully received - * packets to be retransmitted." As of 0.40 all previously received - * packets are allowed, not just the most recent one. + * The RFC also mentions "it is possible for successfully + * received packets to be retransmitted." As of 0.40 all + * previously received packets are allowed, not just the + * most recent one. * - * We allow multiple assembly processes, one for each ARCnet card - * possible on the network. Seems rather like a waste of memory, - * but there's no other way to be reliable. + * We allow multiple assembly processes, one for each + * ARCnet card possible on the network. + * Seems rather like a waste of memory, but there's no + * other way to be reliable. */ - BUGMSG(D_RX, "packet is split (splitflag=%d, seq=%d)\n", - soft->split_flag, in->sequence); + arc_printk(D_RX, dev, "packet is split (splitflag=%d, seq=%d)\n", + soft->split_flag, in->sequence); if (in->skb && in->sequence != soft->sequence) { - BUGMSG(D_EXTRA, "wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n", - saddr, in->sequence, soft->sequence, - soft->split_flag); + arc_printk(D_EXTRA, dev, "wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n", + saddr, in->sequence, soft->sequence, + soft->split_flag); dev_kfree_skb_irq(in->skb); in->skb = NULL; dev->stats.rx_errors++; @@ -262,24 +266,23 @@ static void rx(struct net_device *dev, int bufnum, in->lastpacket = in->numpackets = 0; } if (soft->split_flag & 1) { /* first packet in split */ - BUGMSG(D_RX, "brand new splitpacket (splitflag=%d)\n", - soft->split_flag); + arc_printk(D_RX, dev, "brand new splitpacket (splitflag=%d)\n", + soft->split_flag); if (in->skb) { /* already assembling one! */ - BUGMSG(D_EXTRA, "aborting previous (seq=%d) assembly " - "(splitflag=%d, seq=%d)\n", - in->sequence, soft->split_flag, - soft->sequence); + arc_printk(D_EXTRA, dev, "aborting previous (seq=%d) assembly (splitflag=%d, seq=%d)\n", + in->sequence, soft->split_flag, + soft->sequence); dev->stats.rx_errors++; dev->stats.rx_missed_errors++; dev_kfree_skb_irq(in->skb); } in->sequence = soft->sequence; - in->numpackets = ((unsigned) soft->split_flag >> 1) + 2; + in->numpackets = ((unsigned)soft->split_flag >> 1) + 2; in->lastpacket = 1; if (in->numpackets > 16) { - BUGMSG(D_EXTRA, "incoming packet more than 16 segments; dropping. (splitflag=%d)\n", - soft->split_flag); + arc_printk(D_EXTRA, dev, "incoming packet more than 16 segments; dropping. (splitflag=%d)\n", + soft->split_flag); lp->rfc1201.aborted_seq = soft->sequence; dev->stats.rx_errors++; dev->stats.rx_length_errors++; @@ -287,14 +290,14 @@ static void rx(struct net_device *dev, int bufnum, } in->skb = skb = alloc_skb(508 * in->numpackets + ARC_HDR_SIZE, GFP_ATOMIC); - if (skb == NULL) { - BUGMSG(D_NORMAL, "(split) memory squeeze, dropping packet.\n"); + if (!skb) { + arc_printk(D_NORMAL, dev, "(split) memory squeeze, dropping packet.\n"); lp->rfc1201.aborted_seq = soft->sequence; dev->stats.rx_dropped++; return; } skb->dev = dev; - pkt = (struct archdr *) skb->data; + pkt = (struct archdr *)skb->data; soft = &pkt->soft.rfc1201; memcpy(pkt, pkthdr, ARC_HDR_SIZE + RFC1201_HDR_SIZE); @@ -302,37 +305,37 @@ static void rx(struct net_device *dev, int bufnum, soft->split_flag = 0; /* end result won't be split */ } else { /* not first packet */ - int packetnum = ((unsigned) soft->split_flag >> 1) + 1; + int packetnum = ((unsigned)soft->split_flag >> 1) + 1; - /* - * if we're not assembling, there's no point trying to + /* if we're not assembling, there's no point trying to * continue. */ if (!in->skb) { if (lp->rfc1201.aborted_seq != soft->sequence) { - BUGMSG(D_EXTRA, "can't continue split without starting " - "first! (splitflag=%d, seq=%d, aborted=%d)\n", - soft->split_flag, soft->sequence, - lp->rfc1201.aborted_seq); + arc_printk(D_EXTRA, dev, "can't continue split without starting first! (splitflag=%d, seq=%d, aborted=%d)\n", + soft->split_flag, + soft->sequence, + lp->rfc1201.aborted_seq); dev->stats.rx_errors++; dev->stats.rx_missed_errors++; } return; } in->lastpacket++; - if (packetnum != in->lastpacket) { /* not the right flag! */ + /* if not the right flag */ + if (packetnum != in->lastpacket) { /* harmless duplicate? ignore. */ if (packetnum <= in->lastpacket - 1) { - BUGMSG(D_EXTRA, "duplicate splitpacket ignored! (splitflag=%d)\n", - soft->split_flag); + arc_printk(D_EXTRA, dev, "duplicate splitpacket ignored! (splitflag=%d)\n", + soft->split_flag); dev->stats.rx_errors++; dev->stats.rx_frame_errors++; return; } /* "bad" duplicate, kill reassembly */ - BUGMSG(D_EXTRA, "out-of-order splitpacket, reassembly " - "(seq=%d) aborted (splitflag=%d, seq=%d)\n", - in->sequence, soft->split_flag, soft->sequence); + arc_printk(D_EXTRA, dev, "out-of-order splitpacket, reassembly (seq=%d) aborted (splitflag=%d, seq=%d)\n", + in->sequence, soft->split_flag, + soft->sequence); lp->rfc1201.aborted_seq = soft->sequence; dev_kfree_skb_irq(in->skb); in->skb = NULL; @@ -341,7 +344,7 @@ static void rx(struct net_device *dev, int bufnum, in->lastpacket = in->numpackets = 0; return; } - pkt = (struct archdr *) in->skb->data; + pkt = (struct archdr *)in->skb->data; soft = &pkt->soft.rfc1201; } @@ -357,11 +360,12 @@ static void rx(struct net_device *dev, int bufnum, in->skb = NULL; in->lastpacket = in->numpackets = 0; - BUGMSG(D_SKB_SIZE, "skb: received %d bytes from %02X (unsplit)\n", - skb->len, pkt->hard.source); - BUGMSG(D_SKB_SIZE, "skb: received %d bytes from %02X (split)\n", - skb->len, pkt->hard.source); - BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx"); + arc_printk(D_SKB_SIZE, dev, "skb: received %d bytes from %02X (unsplit)\n", + skb->len, pkt->hard.source); + arc_printk(D_SKB_SIZE, dev, "skb: received %d bytes from %02X (split)\n", + skb->len, pkt->hard.source); + if (BUGLVL(D_SKB)) + arcnet_dump_skb(dev, skb, "rx"); skb->protocol = type_trans(skb, dev); netif_rx(skb); @@ -369,14 +373,13 @@ static void rx(struct net_device *dev, int bufnum, } } - /* Create the ARCnet hard/soft headers for RFC1201. */ static int build_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, uint8_t daddr) { struct arcnet_local *lp = netdev_priv(dev); int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE; - struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size); + struct archdr *pkt = (struct archdr *)skb_push(skb, hdr_size); struct arc_rfc1201 *soft = &pkt->soft.rfc1201; /* set the protocol ID according to RFC1201 */ @@ -402,19 +405,18 @@ static int build_header(struct sk_buff *skb, struct net_device *dev, soft->proto = ARC_P_ATALK; break; default: - BUGMSG(D_NORMAL, "RFC1201: I don't understand protocol %d (%Xh)\n", - type, type); + arc_printk(D_NORMAL, dev, "RFC1201: I don't understand protocol %d (%Xh)\n", + type, type); dev->stats.tx_errors++; dev->stats.tx_aborted_errors++; return 0; } - /* - * Set the source hardware address. + /* Set the source hardware address. * * This is pretty pointless for most purposes, but it can help in - * debugging. ARCnet does not allow us to change the source address in - * the actual packet sent) + * debugging. ARCnet does not allow us to change the source address + * in the actual packet sent. */ pkt->hard.source = *dev->dev_addr; @@ -424,10 +426,10 @@ static int build_header(struct sk_buff *skb, struct net_device *dev, /* see linux/net/ethernet/eth.c to see where I got the following */ if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { - /* - * FIXME: fill in the last byte of the dest ipaddr here to better - * comply with RFC1051 in "noarp" mode. For now, always broadcasting - * will probably at least get packets sent out :) + /* FIXME: fill in the last byte of the dest ipaddr here + * to better comply with RFC1051 in "noarp" mode. + * For now, always broadcasting will probably at least get + * packets sent out :) */ pkt->hard.dest = 0; return hdr_size; @@ -437,7 +439,6 @@ static int build_header(struct sk_buff *skb, struct net_device *dev, return hdr_size; } - static void load_pkt(struct net_device *dev, struct arc_hardware *hard, struct arc_rfc1201 *soft, int softlen, int bufnum) { @@ -461,8 +462,9 @@ static void load_pkt(struct net_device *dev, struct arc_hardware *hard, hard->offset[1] = ofs - RFC1201_HDR_SIZE; lp->hw.copy_to_card(dev, bufnum, ofs - RFC1201_HDR_SIZE, &excsoft, RFC1201_HDR_SIZE); - } else + } else { hard->offset[0] = ofs = 256 - softlen; + } lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE); lp->hw.copy_to_card(dev, bufnum, ofs, soft, softlen); @@ -470,7 +472,6 @@ static void load_pkt(struct net_device *dev, struct arc_hardware *hard, lp->lastload_dest = hard->dest; } - static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, int bufnum) { @@ -478,11 +479,11 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, const int maxsegsize = XMTU - RFC1201_HDR_SIZE; struct Outgoing *out; + arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n", + lp->next_tx, lp->cur_tx, bufnum); - BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n", - lp->next_tx, lp->cur_tx, bufnum); - - length -= ARC_HDR_SIZE; /* hard header is not included in packet length */ + /* hard header is not included in packet length */ + length -= ARC_HDR_SIZE; pkt->soft.rfc1201.split_flag = 0; /* need to do a split packet? */ @@ -494,9 +495,9 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, out->numsegs = (out->dataleft + maxsegsize - 1) / maxsegsize; out->segnum = 0; - BUGMSG(D_DURING, "rfc1201 prep_tx: ready for %d-segment split " - "(%d bytes, seq=%d)\n", out->numsegs, out->length, - pkt->soft.rfc1201.sequence); + arc_printk(D_DURING, dev, "rfc1201 prep_tx: ready for %d-segment split (%d bytes, seq=%d)\n", + out->numsegs, out->length, + pkt->soft.rfc1201.sequence); return 0; /* not done */ } @@ -506,7 +507,6 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, return 1; /* done */ } - static int continue_tx(struct net_device *dev, int bufnum) { struct arcnet_local *lp = netdev_priv(dev); @@ -516,9 +516,9 @@ static int continue_tx(struct net_device *dev, int bufnum) int maxsegsize = XMTU - RFC1201_HDR_SIZE; int seglen; - BUGMSG(D_DURING, - "rfc1201 continue_tx: loading segment %d(+1) of %d (seq=%d)\n", - out->segnum, out->numsegs, soft->sequence); + arc_printk(D_DURING, dev, + "rfc1201 continue_tx: loading segment %d(+1) of %d (seq=%d)\n", + out->segnum, out->numsegs, soft->sequence); /* the "new" soft header comes right before the data chunk */ newsoft = (struct arc_rfc1201 *) diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index e8c96b8e86f4..6d04183ed955 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -129,6 +129,16 @@ config CAN_RCAR To compile this driver as a module, choose M here: the module will be called rcar_can. +config CAN_SUN4I + tristate "Allwinner A10 CAN controller" + depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST + ---help--- + Say Y here if you want to use CAN controller found on Allwinner + A10/A20 SoCs. + + To compile this driver as a module, choose M here: the module will + be called sun4i_can. + config CAN_XILINXCAN tristate "Xilinx CAN" depends on ARCH_ZYNQ || ARM64 || MICROBLAZE || COMPILE_TEST diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index c533c62b0f5e..1f21cef1d458 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o obj-$(CONFIG_PCH_CAN) += pch_can.o obj-$(CONFIG_CAN_GRCAN) += grcan.o obj-$(CONFIG_CAN_RCAR) += rcar_can.o +obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index c83f0f03482b..868fe945e35a 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -26,12 +26,8 @@ #include <linux/can/led.h> #include <linux/clk.h> #include <linux/delay.h> -#include <linux/if_arp.h> -#include <linux/if_ether.h> #include <linux/interrupt.h> #include <linux/io.h> -#include <linux/kernel.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> @@ -63,10 +59,10 @@ #define FLEXCAN_MCR_LPRIO_EN BIT(13) #define FLEXCAN_MCR_AEN BIT(12) #define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f) -#define FLEXCAN_MCR_IDAM_A (0 << 8) -#define FLEXCAN_MCR_IDAM_B (1 << 8) -#define FLEXCAN_MCR_IDAM_C (2 << 8) -#define FLEXCAN_MCR_IDAM_D (3 << 8) +#define FLEXCAN_MCR_IDAM_A (0x0 << 8) +#define FLEXCAN_MCR_IDAM_B (0x1 << 8) +#define FLEXCAN_MCR_IDAM_C (0x2 << 8) +#define FLEXCAN_MCR_IDAM_D (0x3 << 8) /* FLEXCAN control register (CANCTRL) bits */ #define FLEXCAN_CTRL_PRESDIV(x) (((x) & 0xff) << 24) @@ -161,7 +157,7 @@ #define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24) #define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24) #define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24) -#define FLEXCAN_MB_CODE_RX_OVERRRUN (0x6 << 24) +#define FLEXCAN_MB_CODE_RX_OVERRUN (0x6 << 24) #define FLEXCAN_MB_CODE_RX_RANSWER (0xa << 24) #define FLEXCAN_MB_CODE_TX_INACTIVE (0x8 << 24) @@ -175,12 +171,9 @@ #define FLEXCAN_MB_CNT_LENGTH(x) (((x) & 0xf) << 16) #define FLEXCAN_MB_CNT_TIMESTAMP(x) ((x) & 0xffff) -#define FLEXCAN_MB_CODE_MASK (0xf0ffffff) +#define FLEXCAN_TIMEOUT_US (50) -#define FLEXCAN_TIMEOUT_US (50) - -/* - * FLEXCAN hardware feature flags +/* FLEXCAN hardware feature flags * * Below is some version info we got: * SOC Version IP-Version Glitch- [TR]WRN_INT Memory err RTR re- @@ -194,9 +187,9 @@ * * Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected. */ -#define FLEXCAN_HAS_V10_FEATURES BIT(1) /* For core version >= 10 */ -#define FLEXCAN_HAS_BROKEN_ERR_STATE BIT(2) /* [TR]WRN_INT not connected */ -#define FLEXCAN_HAS_MECR_FEATURES BIT(3) /* Memory error detection */ +#define FLEXCAN_QUIRK_BROKEN_ERR_STATE BIT(1) /* [TR]WRN_INT not connected */ +#define FLEXCAN_QUIRK_DISABLE_RXFG BIT(2) /* Disable RX FIFO Global mask */ +#define FLEXCAN_QUIRK_DISABLE_MECR BIT(3) /* Disble Memory error detection */ /* Structure of the message buffer */ struct flexcan_mb { @@ -228,7 +221,7 @@ struct flexcan_regs { u32 rxfgmask; /* 0x48 */ u32 rxfir; /* 0x4c */ u32 _reserved3[12]; /* 0x50 */ - struct flexcan_mb cantxfg[64]; /* 0x80 */ + struct flexcan_mb mb[64]; /* 0x80 */ /* FIFO-mode: * MB * 0x080...0x08f 0 RX message buffer @@ -236,7 +229,7 @@ struct flexcan_regs { * 0x0e0...0x0ff 6-7 8 entry ID table * (mx25, mx28, mx35, mx53) * 0x0e0...0x2df 6-7..37 8..128 entry ID table - * size conf'ed via ctrl2::RFFN + * size conf'ed via ctrl2::RFFN * (mx6, vf610) */ u32 _reserved4[408]; @@ -251,14 +244,14 @@ struct flexcan_regs { }; struct flexcan_devtype_data { - u32 features; /* hardware controller features */ + u32 quirks; /* quirks needed for different IP cores */ }; struct flexcan_priv { struct can_priv can; struct napi_struct napi; - void __iomem *base; + struct flexcan_regs __iomem *regs; u32 reg_esr; u32 reg_ctrl_default; @@ -270,14 +263,17 @@ struct flexcan_priv { }; static struct flexcan_devtype_data fsl_p1010_devtype_data = { - .features = FLEXCAN_HAS_BROKEN_ERR_STATE, + .quirks = FLEXCAN_QUIRK_BROKEN_ERR_STATE, }; + static struct flexcan_devtype_data fsl_imx28_devtype_data; + static struct flexcan_devtype_data fsl_imx6q_devtype_data = { - .features = FLEXCAN_HAS_V10_FEATURES, + .quirks = FLEXCAN_QUIRK_DISABLE_RXFG, }; + static struct flexcan_devtype_data fsl_vf610_devtype_data = { - .features = FLEXCAN_HAS_V10_FEATURES | FLEXCAN_HAS_MECR_FEATURES, + .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_DISABLE_MECR, }; static const struct can_bittiming_const flexcan_bittiming_const = { @@ -292,11 +288,10 @@ static const struct can_bittiming_const flexcan_bittiming_const = { .brp_inc = 1, }; -/* - * Abstract off the read/write for arm versus ppc. This +/* Abstract off the read/write for arm versus ppc. This * assumes that PPC uses big-endian registers and everything * else uses little-endian registers, independent of CPU - * endianess. + * endianness. */ #if defined(CONFIG_PPC) static inline u32 flexcan_read(void __iomem *addr) @@ -345,7 +340,7 @@ static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv, static int flexcan_chip_enable(struct flexcan_priv *priv) { - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; @@ -364,7 +359,7 @@ static int flexcan_chip_enable(struct flexcan_priv *priv) static int flexcan_chip_disable(struct flexcan_priv *priv) { - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; @@ -383,7 +378,7 @@ static int flexcan_chip_disable(struct flexcan_priv *priv) static int flexcan_chip_freeze(struct flexcan_priv *priv) { - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; unsigned int timeout = 1000 * 1000 * 10 / priv->can.bittiming.bitrate; u32 reg; @@ -402,7 +397,7 @@ static int flexcan_chip_freeze(struct flexcan_priv *priv) static int flexcan_chip_unfreeze(struct flexcan_priv *priv) { - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; @@ -421,7 +416,7 @@ static int flexcan_chip_unfreeze(struct flexcan_priv *priv) static int flexcan_chip_softreset(struct flexcan_priv *priv) { - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr); @@ -434,12 +429,11 @@ static int flexcan_chip_softreset(struct flexcan_priv *priv) return 0; } - static int __flexcan_get_berr_counter(const struct net_device *dev, struct can_berr_counter *bec) { const struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg = flexcan_read(®s->ecr); bec->txerr = (reg >> 0) & 0xff; @@ -474,9 +468,10 @@ static int flexcan_get_berr_counter(const struct net_device *dev, static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) { const struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; struct can_frame *cf = (struct can_frame *)skb->data; u32 can_id; + u32 data; u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | (cf->can_dlc << 16); if (can_dropped_invalid_skb(dev, skb)) @@ -495,26 +490,26 @@ static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) ctrl |= FLEXCAN_MB_CNT_RTR; if (cf->can_dlc > 0) { - u32 data = be32_to_cpup((__be32 *)&cf->data[0]); - flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[0]); + data = be32_to_cpup((__be32 *)&cf->data[0]); + flexcan_write(data, ®s->mb[FLEXCAN_TX_BUF_ID].data[0]); } if (cf->can_dlc > 3) { - u32 data = be32_to_cpup((__be32 *)&cf->data[4]); - flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[1]); + data = be32_to_cpup((__be32 *)&cf->data[4]); + flexcan_write(data, ®s->mb[FLEXCAN_TX_BUF_ID].data[1]); } can_put_echo_skb(skb, dev, 0); - flexcan_write(can_id, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_id); - flexcan_write(ctrl, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); + flexcan_write(can_id, ®s->mb[FLEXCAN_TX_BUF_ID].can_id); + flexcan_write(ctrl, ®s->mb[FLEXCAN_TX_BUF_ID].can_ctrl); /* Errata ERR005829 step8: * Write twice INACTIVE(0x8) code to first MB. */ flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, - ®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl); + ®s->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl); flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, - ®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl); + ®s->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl); return NETDEV_TX_OK; } @@ -597,14 +592,14 @@ static int flexcan_poll_state(struct net_device *dev, u32 reg_esr) flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK; if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) { tx_state = unlikely(reg_esr & FLEXCAN_ESR_TX_WRN) ? - CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; + CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ? - CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; + CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; new_state = max(tx_state, rx_state); } else { __flexcan_get_berr_counter(dev, &bec); new_state = flt == FLEXCAN_ESR_FLT_CONF_PASSIVE ? - CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF; + 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; } @@ -633,8 +628,8 @@ static void flexcan_read_fifo(const struct net_device *dev, struct can_frame *cf) { const struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; - struct flexcan_mb __iomem *mb = ®s->cantxfg[0]; + struct flexcan_regs __iomem *regs = priv->regs; + struct flexcan_mb __iomem *mb = ®s->mb[0]; u32 reg_ctrl, reg_id; reg_ctrl = flexcan_read(&mb->can_ctrl); @@ -683,12 +678,11 @@ static int flexcan_poll(struct napi_struct *napi, int quota) { struct net_device *dev = napi->dev; const struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg_iflag1, reg_esr; int work_done = 0; - /* - * The error bits are cleared on read, + /* The error bits are cleared on read, * use saved value from irq handler. */ reg_esr = flexcan_read(®s->esr) | priv->reg_esr; @@ -723,17 +717,17 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) struct net_device *dev = dev_id; struct net_device_stats *stats = &dev->stats; struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg_iflag1, reg_esr; reg_iflag1 = flexcan_read(®s->iflag1); reg_esr = flexcan_read(®s->esr); + /* ACK all bus error and state change IRQ sources */ if (reg_esr & FLEXCAN_ESR_ALL_INT) flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); - /* - * schedule NAPI in case of: + /* schedule NAPI in case of: * - rx IRQ * - state change IRQ * - bus error IRQ and bus error reporting is activated @@ -741,15 +735,14 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) || (reg_esr & FLEXCAN_ESR_ERR_STATE) || flexcan_has_and_handle_berr(priv, reg_esr)) { - /* - * The error bits are cleared on read, + /* The error bits are cleared on read, * save them for later use. */ priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS; flexcan_write(FLEXCAN_IFLAG_DEFAULT & - ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->imask1); + ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->imask1); flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, - ®s->ctrl); + ®s->ctrl); napi_schedule(&priv->napi); } @@ -765,9 +758,10 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) stats->tx_bytes += can_get_echo_skb(dev, 0); stats->tx_packets++; can_led_event(dev, CAN_LED_EVENT_TX); - /* after sending a RTR frame mailbox is in RX mode */ + + /* after sending a RTR frame MB is in RX mode */ flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, - ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); + ®s->mb[FLEXCAN_TX_BUF_ID].can_ctrl); flexcan_write((1 << FLEXCAN_TX_BUF_ID), ®s->iflag1); netif_wake_queue(dev); } @@ -779,7 +773,7 @@ static void flexcan_set_bittiming(struct net_device *dev) { const struct flexcan_priv *priv = netdev_priv(dev); const struct can_bittiming *bt = &priv->can.bittiming; - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg; reg = flexcan_read(®s->ctrl); @@ -813,8 +807,7 @@ static void flexcan_set_bittiming(struct net_device *dev) flexcan_read(®s->mcr), flexcan_read(®s->ctrl)); } -/* - * flexcan_chip_start +/* flexcan_chip_start * * this functions is entered with clocks enabled * @@ -822,7 +815,7 @@ static void flexcan_set_bittiming(struct net_device *dev) static int flexcan_chip_start(struct net_device *dev) { struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr; int err, i; @@ -838,29 +831,26 @@ static int flexcan_chip_start(struct net_device *dev) flexcan_set_bittiming(dev); - /* - * MCR + /* MCR * * enable freeze * enable fifo * halt now * only supervisor access * enable warning int - * choose format C * disable local echo - * + * choose format C + * set max mailbox number */ reg_mcr = flexcan_read(®s->mcr); reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT | - FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | - FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS | - FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); + FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | + FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); flexcan_write(reg_mcr, ®s->mcr); - /* - * CTRL + /* CTRL * * disable timer sync feature * @@ -875,12 +865,12 @@ static int flexcan_chip_start(struct net_device *dev) reg_ctrl &= ~FLEXCAN_CTRL_TSYN; reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF | FLEXCAN_CTRL_ERR_STATE; - /* - * enable the "error interrupt" (FLEXCAN_CTRL_ERR_MSK), + + /* enable the "error interrupt" (FLEXCAN_CTRL_ERR_MSK), * on most Flexcan cores, too. Otherwise we don't get * any error warning or passive interrupts. */ - if (priv->devtype_data->features & FLEXCAN_HAS_BROKEN_ERR_STATE || + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_BROKEN_ERR_STATE || priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) reg_ctrl |= FLEXCAN_CTRL_ERR_MSK; else @@ -888,41 +878,41 @@ static int flexcan_chip_start(struct net_device *dev) /* save for later use */ priv->reg_ctrl_default = reg_ctrl; + /* leave interrupts disabled for now */ + reg_ctrl &= ~FLEXCAN_CTRL_ERR_ALL; netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl); flexcan_write(reg_ctrl, ®s->ctrl); /* clear and invalidate all mailboxes first */ - for (i = FLEXCAN_TX_BUF_ID; i < ARRAY_SIZE(regs->cantxfg); i++) { + for (i = FLEXCAN_TX_BUF_ID; i < ARRAY_SIZE(regs->mb); i++) { flexcan_write(FLEXCAN_MB_CODE_RX_INACTIVE, - ®s->cantxfg[i].can_ctrl); + ®s->mb[i].can_ctrl); } /* Errata ERR005829: mark first TX mailbox as INACTIVE */ flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, - ®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl); + ®s->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl); /* mark TX mailbox as INACTIVE */ flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, - ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); + ®s->mb[FLEXCAN_TX_BUF_ID].can_ctrl); /* acceptance mask/acceptance code (accept everything) */ flexcan_write(0x0, ®s->rxgmask); flexcan_write(0x0, ®s->rx14mask); flexcan_write(0x0, ®s->rx15mask); - if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG) flexcan_write(0x0, ®s->rxfgmask); - /* - * On Vybrid, disable memory error detection interrupts + /* On Vybrid, disable memory error detection interrupts * and freeze mode. * This also works around errata e5295 which generates * false positive memory errors and put the device in * freeze mode. */ - if (priv->devtype_data->features & FLEXCAN_HAS_MECR_FEATURES) { - /* - * Follow the protocol as described in "Detection + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_MECR) { + /* Follow the protocol as described in "Detection * and Correction of Memory Errors" to write to * MECR register */ @@ -934,7 +924,7 @@ static int flexcan_chip_start(struct net_device *dev) reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS; flexcan_write(reg_mecr, ®s->mecr); reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK | - FLEXCAN_MECR_FANCEI_MSK); + FLEXCAN_MECR_FANCEI_MSK); flexcan_write(reg_mecr, ®s->mecr); } @@ -949,8 +939,11 @@ static int flexcan_chip_start(struct net_device *dev) priv->can.state = CAN_STATE_ERROR_ACTIVE; - /* enable FIFO interrupts */ + /* enable interrupts atomically */ + disable_irq(dev->irq); + flexcan_write(priv->reg_ctrl_default, ®s->ctrl); flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); + enable_irq(dev->irq); /* print chip status */ netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__, @@ -965,16 +958,14 @@ static int flexcan_chip_start(struct net_device *dev) return err; } -/* - * flexcan_chip_stop +/* flexcan_chip_stop * * this functions is entered with clocks enabled - * */ static void flexcan_chip_stop(struct net_device *dev) { struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; /* freeze + disable module */ flexcan_chip_freeze(priv); @@ -987,8 +978,6 @@ static void flexcan_chip_stop(struct net_device *dev) flexcan_transceiver_disable(priv); priv->can.state = CAN_STATE_STOPPED; - - return; } static int flexcan_open(struct net_device *dev) @@ -1085,7 +1074,7 @@ static const struct net_device_ops flexcan_netdev_ops = { static int register_flexcandev(struct net_device *dev) { struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg, err; err = clk_prepare_enable(priv->clk_ipg); @@ -1114,8 +1103,7 @@ static int register_flexcandev(struct net_device *dev) FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV; flexcan_write(reg, ®s->mcr); - /* - * Currently we only support newer versions of this core + /* Currently we only support newer versions of this core * featuring a RX FIFO. Older cores found on some Coldfire * derivates are not yet supported. */ @@ -1168,7 +1156,7 @@ static int flexcan_probe(struct platform_device *pdev) struct regulator *reg_xceiver; struct resource *mem; struct clk *clk_ipg = NULL, *clk_per = NULL; - void __iomem *base; + struct flexcan_regs __iomem *regs; int err, irq; u32 clock_freq = 0; @@ -1180,7 +1168,7 @@ static int flexcan_probe(struct platform_device *pdev) if (pdev->dev.of_node) of_property_read_u32(pdev->dev.of_node, - "clock-frequency", &clock_freq); + "clock-frequency", &clock_freq); if (!clock_freq) { clk_ipg = devm_clk_get(&pdev->dev, "ipg"); @@ -1202,9 +1190,9 @@ static int flexcan_probe(struct platform_device *pdev) if (irq <= 0) return -ENODEV; - base = devm_ioremap_resource(&pdev->dev, mem); - if (IS_ERR(base)) - return PTR_ERR(base); + regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(regs)) + return PTR_ERR(regs); of_id = of_match_device(flexcan_of_match, &pdev->dev); if (of_id) { @@ -1232,12 +1220,11 @@ static int flexcan_probe(struct platform_device *pdev) priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_BERR_REPORTING; - priv->base = base; + priv->regs = regs; priv->clk_ipg = clk_ipg; priv->clk_per = clk_per; priv->pdata = dev_get_platdata(&pdev->dev); priv->devtype_data = devtype_data; - priv->reg_xceiver = reg_xceiver; netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT); @@ -1254,7 +1241,7 @@ static int flexcan_probe(struct platform_device *pdev) devm_can_led_init(dev); dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n", - priv->base, dev->irq); + priv->regs, dev->irq); return 0; diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c new file mode 100644 index 000000000000..10d8497635e8 --- /dev/null +++ b/drivers/net/can/sun4i_can.c @@ -0,0 +1,857 @@ +/* + * sun4i_can.c - CAN bus controller driver for Allwinner SUN4I&SUN7I based SoCs + * + * Copyright (C) 2013 Peter Chen + * Copyright (C) 2015 Gerhard Bertelsmann + * All rights reserved. + * + * Parts of this software are based on (derived from) the SJA1000 code by: + * Copyright (C) 2014 Oliver Hartkopp <oliver.hartkopp@volkswagen.de> + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2002-2007 Volkswagen Group Electronic Research + * Copyright (C) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33, + * 38106 Braunschweig, GERMANY + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + */ + +#include <linux/netdevice.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/led.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#define DRV_NAME "sun4i_can" + +/* Registers address (physical base address 0x01C2BC00) */ +#define SUN4I_REG_MSEL_ADDR 0x0000 /* CAN Mode Select */ +#define SUN4I_REG_CMD_ADDR 0x0004 /* CAN Command */ +#define SUN4I_REG_STA_ADDR 0x0008 /* CAN Status */ +#define SUN4I_REG_INT_ADDR 0x000c /* CAN Interrupt Flag */ +#define SUN4I_REG_INTEN_ADDR 0x0010 /* CAN Interrupt Enable */ +#define SUN4I_REG_BTIME_ADDR 0x0014 /* CAN Bus Timing 0 */ +#define SUN4I_REG_TEWL_ADDR 0x0018 /* CAN Tx Error Warning Limit */ +#define SUN4I_REG_ERRC_ADDR 0x001c /* CAN Error Counter */ +#define SUN4I_REG_RMCNT_ADDR 0x0020 /* CAN Receive Message Counter */ +#define SUN4I_REG_RBUFSA_ADDR 0x0024 /* CAN Receive Buffer Start Address */ +#define SUN4I_REG_BUF0_ADDR 0x0040 /* CAN Tx/Rx Buffer 0 */ +#define SUN4I_REG_BUF1_ADDR 0x0044 /* CAN Tx/Rx Buffer 1 */ +#define SUN4I_REG_BUF2_ADDR 0x0048 /* CAN Tx/Rx Buffer 2 */ +#define SUN4I_REG_BUF3_ADDR 0x004c /* CAN Tx/Rx Buffer 3 */ +#define SUN4I_REG_BUF4_ADDR 0x0050 /* CAN Tx/Rx Buffer 4 */ +#define SUN4I_REG_BUF5_ADDR 0x0054 /* CAN Tx/Rx Buffer 5 */ +#define SUN4I_REG_BUF6_ADDR 0x0058 /* CAN Tx/Rx Buffer 6 */ +#define SUN4I_REG_BUF7_ADDR 0x005c /* CAN Tx/Rx Buffer 7 */ +#define SUN4I_REG_BUF8_ADDR 0x0060 /* CAN Tx/Rx Buffer 8 */ +#define SUN4I_REG_BUF9_ADDR 0x0064 /* CAN Tx/Rx Buffer 9 */ +#define SUN4I_REG_BUF10_ADDR 0x0068 /* CAN Tx/Rx Buffer 10 */ +#define SUN4I_REG_BUF11_ADDR 0x006c /* CAN Tx/Rx Buffer 11 */ +#define SUN4I_REG_BUF12_ADDR 0x0070 /* CAN Tx/Rx Buffer 12 */ +#define SUN4I_REG_ACPC_ADDR 0x0040 /* CAN Acceptance Code 0 */ +#define SUN4I_REG_ACPM_ADDR 0x0044 /* CAN Acceptance Mask 0 */ +#define SUN4I_REG_RBUF_RBACK_START_ADDR 0x0180 /* CAN transmit buffer start */ +#define SUN4I_REG_RBUF_RBACK_END_ADDR 0x01b0 /* CAN transmit buffer end */ + +/* Controller Register Description */ + +/* mode select register (r/w) + * offset:0x0000 default:0x0000_0001 + */ +#define SUN4I_MSEL_SLEEP_MODE (0x01 << 4) /* write in reset mode */ +#define SUN4I_MSEL_WAKE_UP (0x00 << 4) +#define SUN4I_MSEL_SINGLE_FILTER (0x01 << 3) /* write in reset mode */ +#define SUN4I_MSEL_DUAL_FILTERS (0x00 << 3) +#define SUN4I_MSEL_LOOPBACK_MODE BIT(2) +#define SUN4I_MSEL_LISTEN_ONLY_MODE BIT(1) +#define SUN4I_MSEL_RESET_MODE BIT(0) + +/* command register (w) + * offset:0x0004 default:0x0000_0000 + */ +#define SUN4I_CMD_BUS_OFF_REQ BIT(5) +#define SUN4I_CMD_SELF_RCV_REQ BIT(4) +#define SUN4I_CMD_CLEAR_OR_FLAG BIT(3) +#define SUN4I_CMD_RELEASE_RBUF BIT(2) +#define SUN4I_CMD_ABORT_REQ BIT(1) +#define SUN4I_CMD_TRANS_REQ BIT(0) + +/* status register (r) + * offset:0x0008 default:0x0000_003c + */ +#define SUN4I_STA_BIT_ERR (0x00 << 22) +#define SUN4I_STA_FORM_ERR (0x01 << 22) +#define SUN4I_STA_STUFF_ERR (0x02 << 22) +#define SUN4I_STA_OTHER_ERR (0x03 << 22) +#define SUN4I_STA_MASK_ERR (0x03 << 22) +#define SUN4I_STA_ERR_DIR BIT(21) +#define SUN4I_STA_ERR_SEG_CODE (0x1f << 16) +#define SUN4I_STA_START (0x03 << 16) +#define SUN4I_STA_ID28_21 (0x02 << 16) +#define SUN4I_STA_ID20_18 (0x06 << 16) +#define SUN4I_STA_SRTR (0x04 << 16) +#define SUN4I_STA_IDE (0x05 << 16) +#define SUN4I_STA_ID17_13 (0x07 << 16) +#define SUN4I_STA_ID12_5 (0x0f << 16) +#define SUN4I_STA_ID4_0 (0x0e << 16) +#define SUN4I_STA_RTR (0x0c << 16) +#define SUN4I_STA_RB1 (0x0d << 16) +#define SUN4I_STA_RB0 (0x09 << 16) +#define SUN4I_STA_DLEN (0x0b << 16) +#define SUN4I_STA_DATA_FIELD (0x0a << 16) +#define SUN4I_STA_CRC_SEQUENCE (0x08 << 16) +#define SUN4I_STA_CRC_DELIMITER (0x18 << 16) +#define SUN4I_STA_ACK (0x19 << 16) +#define SUN4I_STA_ACK_DELIMITER (0x1b << 16) +#define SUN4I_STA_END (0x1a << 16) +#define SUN4I_STA_INTERMISSION (0x12 << 16) +#define SUN4I_STA_ACTIVE_ERROR (0x11 << 16) +#define SUN4I_STA_PASSIVE_ERROR (0x16 << 16) +#define SUN4I_STA_TOLERATE_DOMINANT_BITS (0x13 << 16) +#define SUN4I_STA_ERROR_DELIMITER (0x17 << 16) +#define SUN4I_STA_OVERLOAD (0x1c << 16) +#define SUN4I_STA_BUS_OFF BIT(7) +#define SUN4I_STA_ERR_STA BIT(6) +#define SUN4I_STA_TRANS_BUSY BIT(5) +#define SUN4I_STA_RCV_BUSY BIT(4) +#define SUN4I_STA_TRANS_OVER BIT(3) +#define SUN4I_STA_TBUF_RDY BIT(2) +#define SUN4I_STA_DATA_ORUN BIT(1) +#define SUN4I_STA_RBUF_RDY BIT(0) + +/* interrupt register (r) + * offset:0x000c default:0x0000_0000 + */ +#define SUN4I_INT_BUS_ERR BIT(7) +#define SUN4I_INT_ARB_LOST BIT(6) +#define SUN4I_INT_ERR_PASSIVE BIT(5) +#define SUN4I_INT_WAKEUP BIT(4) +#define SUN4I_INT_DATA_OR BIT(3) +#define SUN4I_INT_ERR_WRN BIT(2) +#define SUN4I_INT_TBUF_VLD BIT(1) +#define SUN4I_INT_RBUF_VLD BIT(0) + +/* interrupt enable register (r/w) + * offset:0x0010 default:0x0000_0000 + */ +#define SUN4I_INTEN_BERR BIT(7) +#define SUN4I_INTEN_ARB_LOST BIT(6) +#define SUN4I_INTEN_ERR_PASSIVE BIT(5) +#define SUN4I_INTEN_WAKEUP BIT(4) +#define SUN4I_INTEN_OR BIT(3) +#define SUN4I_INTEN_ERR_WRN BIT(2) +#define SUN4I_INTEN_TX BIT(1) +#define SUN4I_INTEN_RX BIT(0) + +/* error code */ +#define SUN4I_ERR_INRCV (0x1 << 5) +#define SUN4I_ERR_INTRANS (0x0 << 5) + +/* filter mode */ +#define SUN4I_FILTER_CLOSE 0 +#define SUN4I_SINGLE_FLTER_MODE 1 +#define SUN4I_DUAL_FILTER_MODE 2 + +/* message buffer flags */ +#define SUN4I_MSG_EFF_FLAG BIT(7) +#define SUN4I_MSG_RTR_FLAG BIT(6) + +/* max. number of interrupts handled in ISR */ +#define SUN4I_CAN_MAX_IRQ 20 +#define SUN4I_MODE_MAX_RETRIES 100 + +struct sun4ican_priv { + struct can_priv can; + void __iomem *base; + struct clk *clk; + spinlock_t cmdreg_lock; /* lock for concurrent cmd register writes */ +}; + +static const struct can_bittiming_const sun4ican_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +static void sun4i_can_write_cmdreg(struct sun4ican_priv *priv, u8 val) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->cmdreg_lock, flags); + writel(val, priv->base + SUN4I_REG_CMD_ADDR); + spin_unlock_irqrestore(&priv->cmdreg_lock, flags); +} + +static int set_normal_mode(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int retry = SUN4I_MODE_MAX_RETRIES; + u32 mod_reg_val = 0; + + do { + mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); + mod_reg_val &= ~SUN4I_MSEL_RESET_MODE; + writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); + } while (retry-- && (mod_reg_val & SUN4I_MSEL_RESET_MODE)); + + if (readl(priv->base + SUN4I_REG_MSEL_ADDR) & SUN4I_MSEL_RESET_MODE) { + netdev_err(dev, + "setting controller into normal mode failed!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int set_reset_mode(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int retry = SUN4I_MODE_MAX_RETRIES; + u32 mod_reg_val = 0; + + do { + mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); + mod_reg_val |= SUN4I_MSEL_RESET_MODE; + writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); + } while (retry-- && !(mod_reg_val & SUN4I_MSEL_RESET_MODE)); + + if (!(readl(priv->base + SUN4I_REG_MSEL_ADDR) & + SUN4I_MSEL_RESET_MODE)) { + netdev_err(dev, "setting controller into reset mode failed!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +/* bittiming is called in reset_mode only */ +static int sun4ican_set_bittiming(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u32 cfg; + + cfg = ((bt->brp - 1) & 0x3FF) | + (((bt->sjw - 1) & 0x3) << 14) | + (((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) << 16) | + (((bt->phase_seg2 - 1) & 0x7) << 20); + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + cfg |= 0x800000; + + netdev_dbg(dev, "setting BITTIMING=0x%08x\n", cfg); + writel(cfg, priv->base + SUN4I_REG_BTIME_ADDR); + + return 0; +} + +static int sun4ican_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + u32 errors; + int err; + + err = clk_prepare_enable(priv->clk); + if (err) { + netdev_err(dev, "could not enable clock\n"); + return err; + } + + errors = readl(priv->base + SUN4I_REG_ERRC_ADDR); + + bec->txerr = errors & 0xFF; + bec->rxerr = (errors >> 16) & 0xFF; + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int sun4i_can_start(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int err; + u32 mod_reg_val; + + /* we need to enter the reset mode */ + err = set_reset_mode(dev); + if (err) { + netdev_err(dev, "could not enter reset mode\n"); + return err; + } + + /* set filters - we accept all */ + writel(0x00000000, priv->base + SUN4I_REG_ACPC_ADDR); + writel(0xFFFFFFFF, priv->base + SUN4I_REG_ACPM_ADDR); + + /* clear error counters and error code capture */ + writel(0, priv->base + SUN4I_REG_ERRC_ADDR); + + /* enable interrupts */ + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + writel(0xFF, priv->base + SUN4I_REG_INTEN_ADDR); + else + writel(0xFF & ~SUN4I_INTEN_BERR, + priv->base + SUN4I_REG_INTEN_ADDR); + + /* enter the selected mode */ + mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); + if (priv->can.ctrlmode & CAN_CTRLMODE_PRESUME_ACK) + mod_reg_val |= SUN4I_MSEL_LOOPBACK_MODE; + else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + mod_reg_val |= SUN4I_MSEL_LISTEN_ONLY_MODE; + writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); + + err = sun4ican_set_bittiming(dev); + if (err) + return err; + + /* we are ready to enter the normal mode */ + err = set_normal_mode(dev); + if (err) { + netdev_err(dev, "could not enter normal mode\n"); + return err; + } + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; +} + +static int sun4i_can_stop(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int err; + + priv->can.state = CAN_STATE_STOPPED; + /* we need to enter reset mode */ + err = set_reset_mode(dev); + if (err) { + netdev_err(dev, "could not enter reset mode\n"); + return err; + } + + /* disable all interrupts */ + writel(0, priv->base + SUN4I_REG_INTEN_ADDR); + + return 0; +} + +static int sun4ican_set_mode(struct net_device *dev, enum can_mode mode) +{ + int err; + + switch (mode) { + case CAN_MODE_START: + err = sun4i_can_start(dev); + if (err) { + netdev_err(dev, "starting CAN controller failed!\n"); + return err; + } + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + +/* transmit a CAN message + * message layout in the sk_buff should be like this: + * xx xx xx xx ff ll 00 11 22 33 44 55 66 77 + * [ can_id ] [flags] [len] [can data (up to 8 bytes] + */ +static int sun4ican_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + struct can_frame *cf = (struct can_frame *)skb->data; + u8 dlc; + u32 dreg, msg_flag_n; + canid_t id; + int i; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(dev); + + id = cf->can_id; + dlc = cf->can_dlc; + msg_flag_n = dlc; + + if (id & CAN_RTR_FLAG) + msg_flag_n |= SUN4I_MSG_RTR_FLAG; + + if (id & CAN_EFF_FLAG) { + msg_flag_n |= SUN4I_MSG_EFF_FLAG; + dreg = SUN4I_REG_BUF5_ADDR; + writel((id >> 21) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR); + writel((id >> 13) & 0xFF, priv->base + SUN4I_REG_BUF2_ADDR); + writel((id >> 5) & 0xFF, priv->base + SUN4I_REG_BUF3_ADDR); + writel((id << 3) & 0xF8, priv->base + SUN4I_REG_BUF4_ADDR); + } else { + dreg = SUN4I_REG_BUF3_ADDR; + writel((id >> 3) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR); + writel((id << 5) & 0xE0, priv->base + SUN4I_REG_BUF2_ADDR); + } + + for (i = 0; i < dlc; i++) + writel(cf->data[i], priv->base + (dreg + i * 4)); + + writel(msg_flag_n, priv->base + SUN4I_REG_BUF0_ADDR); + + can_put_echo_skb(skb, dev, 0); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + sun4i_can_write_cmdreg(priv, SUN4I_CMD_SELF_RCV_REQ); + else + sun4i_can_write_cmdreg(priv, SUN4I_CMD_TRANS_REQ); + + return NETDEV_TX_OK; +} + +static void sun4i_can_rx(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 fi; + u32 dreg; + canid_t id; + int i; + + /* create zero'ed CAN frame buffer */ + skb = alloc_can_skb(dev, &cf); + if (!skb) + return; + + fi = readl(priv->base + SUN4I_REG_BUF0_ADDR); + cf->can_dlc = get_can_dlc(fi & 0x0F); + if (fi & SUN4I_MSG_EFF_FLAG) { + dreg = SUN4I_REG_BUF5_ADDR; + id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 21) | + (readl(priv->base + SUN4I_REG_BUF2_ADDR) << 13) | + (readl(priv->base + SUN4I_REG_BUF3_ADDR) << 5) | + ((readl(priv->base + SUN4I_REG_BUF4_ADDR) >> 3) & 0x1f); + id |= CAN_EFF_FLAG; + } else { + dreg = SUN4I_REG_BUF3_ADDR; + id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 3) | + ((readl(priv->base + SUN4I_REG_BUF2_ADDR) >> 5) & 0x7); + } + + /* remote frame ? */ + if (fi & SUN4I_MSG_RTR_FLAG) + id |= CAN_RTR_FLAG; + else + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = readl(priv->base + dreg + i * 4); + + cf->can_id = id; + + sun4i_can_write_cmdreg(priv, SUN4I_CMD_RELEASE_RBUF); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + can_led_event(dev, CAN_LED_EVENT_RX); +} + +static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + enum can_state state = priv->can.state; + enum can_state rx_state, tx_state; + unsigned int rxerr, txerr, errc; + u32 ecc, alc; + + /* we don't skip if alloc fails because we want the stats anyhow */ + skb = alloc_can_err_skb(dev, &cf); + + errc = readl(priv->base + SUN4I_REG_ERRC_ADDR); + rxerr = (errc >> 16) & 0xFF; + txerr = errc & 0xFF; + + if (skb) { + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + if (isrc & SUN4I_INT_DATA_OR) { + /* data overrun interrupt */ + netdev_dbg(dev, "data overrun interrupt\n"); + if (likely(skb)) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + } + stats->rx_over_errors++; + stats->rx_errors++; + /* clear bit */ + sun4i_can_write_cmdreg(priv, SUN4I_CMD_CLEAR_OR_FLAG); + } + if (isrc & SUN4I_INT_ERR_WRN) { + /* error warning interrupt */ + netdev_dbg(dev, "error warning interrupt\n"); + + if (status & SUN4I_STA_BUS_OFF) + state = CAN_STATE_BUS_OFF; + else if (status & SUN4I_STA_ERR_STA) + state = CAN_STATE_ERROR_WARNING; + else + state = CAN_STATE_ERROR_ACTIVE; + } + if (isrc & SUN4I_INT_BUS_ERR) { + /* bus error interrupt */ + netdev_dbg(dev, "bus error interrupt\n"); + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + if (likely(skb)) { + ecc = readl(priv->base + SUN4I_REG_STA_ADDR); + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & SUN4I_STA_MASK_ERR) { + case SUN4I_STA_BIT_ERR: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case SUN4I_STA_FORM_ERR: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case SUN4I_STA_STUFF_ERR: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + cf->data[3] = (ecc & SUN4I_STA_ERR_SEG_CODE) + >> 16; + break; + } + /* error occurred during transmission? */ + if ((ecc & SUN4I_STA_ERR_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + } + } + if (isrc & SUN4I_INT_ERR_PASSIVE) { + /* error passive interrupt */ + netdev_dbg(dev, "error passive interrupt\n"); + if (state == CAN_STATE_ERROR_PASSIVE) + state = CAN_STATE_ERROR_WARNING; + else + state = CAN_STATE_ERROR_PASSIVE; + } + if (isrc & SUN4I_INT_ARB_LOST) { + /* arbitration lost interrupt */ + netdev_dbg(dev, "arbitration lost interrupt\n"); + alc = readl(priv->base + SUN4I_REG_STA_ADDR); + priv->can.can_stats.arbitration_lost++; + stats->tx_errors++; + if (likely(skb)) { + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = (alc & 0x1f) >> 8; + } + } + + if (state != priv->can.state) { + tx_state = txerr >= rxerr ? state : 0; + rx_state = txerr <= rxerr ? state : 0; + + if (likely(skb)) + can_change_state(dev, cf, tx_state, rx_state); + else + priv->can.state = state; + if (state == CAN_STATE_BUS_OFF) + can_bus_off(dev); + } + + if (likely(skb)) { + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } else { + return -ENOMEM; + } + + return 0; +} + +static irqreturn_t sun4i_can_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct sun4ican_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + u8 isrc, status; + int n = 0; + + while ((isrc = readl(priv->base + SUN4I_REG_INT_ADDR)) && + (n < SUN4I_CAN_MAX_IRQ)) { + n++; + status = readl(priv->base + SUN4I_REG_STA_ADDR); + + if (isrc & SUN4I_INT_WAKEUP) + netdev_warn(dev, "wakeup interrupt\n"); + + if (isrc & SUN4I_INT_TBUF_VLD) { + /* transmission complete interrupt */ + stats->tx_bytes += + readl(priv->base + + SUN4I_REG_RBUF_RBACK_START_ADDR) & 0xf; + stats->tx_packets++; + can_get_echo_skb(dev, 0); + netif_wake_queue(dev); + can_led_event(dev, CAN_LED_EVENT_TX); + } + if (isrc & SUN4I_INT_RBUF_VLD) { + /* receive interrupt */ + while (status & SUN4I_STA_RBUF_RDY) { + /* RX buffer is not empty */ + sun4i_can_rx(dev); + status = readl(priv->base + SUN4I_REG_STA_ADDR); + } + } + if (isrc & + (SUN4I_INT_DATA_OR | SUN4I_INT_ERR_WRN | SUN4I_INT_BUS_ERR | + SUN4I_INT_ERR_PASSIVE | SUN4I_INT_ARB_LOST)) { + /* error interrupt */ + if (sun4i_can_err(dev, isrc, status)) + netdev_err(dev, "can't allocate buffer - clearing pending interrupts\n"); + } + /* clear interrupts */ + writel(isrc, priv->base + SUN4I_REG_INT_ADDR); + readl(priv->base + SUN4I_REG_INT_ADDR); + } + if (n >= SUN4I_CAN_MAX_IRQ) + netdev_dbg(dev, "%d messages handled in ISR", n); + + return (n) ? IRQ_HANDLED : IRQ_NONE; +} + +static int sun4ican_open(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int err; + + /* common open */ + err = open_candev(dev); + if (err) + return err; + + /* register interrupt handler */ + err = request_irq(dev->irq, sun4i_can_interrupt, 0, dev->name, dev); + if (err) { + netdev_err(dev, "request_irq err: %d\n", err); + goto exit_irq; + } + + /* turn on clocking for CAN peripheral block */ + err = clk_prepare_enable(priv->clk); + if (err) { + netdev_err(dev, "could not enable CAN peripheral clock\n"); + goto exit_clock; + } + + err = sun4i_can_start(dev); + if (err) { + netdev_err(dev, "could not start CAN peripheral\n"); + goto exit_can_start; + } + + can_led_event(dev, CAN_LED_EVENT_OPEN); + netif_start_queue(dev); + + return 0; + +exit_can_start: + clk_disable_unprepare(priv->clk); +exit_clock: + free_irq(dev->irq, dev); +exit_irq: + close_candev(dev); + return err; +} + +static int sun4ican_close(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + sun4i_can_stop(dev); + clk_disable_unprepare(priv->clk); + + free_irq(dev->irq, dev); + close_candev(dev); + can_led_event(dev, CAN_LED_EVENT_STOP); + + return 0; +} + +static const struct net_device_ops sun4ican_netdev_ops = { + .ndo_open = sun4ican_open, + .ndo_stop = sun4ican_close, + .ndo_start_xmit = sun4ican_start_xmit, +}; + +static const struct of_device_id sun4ican_of_match[] = { + {.compatible = "allwinner,sun4i-a10-can"}, + {}, +}; + +MODULE_DEVICE_TABLE(of, sun4ican_of_match); + +static int sun4ican_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + + unregister_netdev(dev); + free_candev(dev); + + return 0; +} + +static int sun4ican_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *mem; + struct clk *clk; + void __iomem *addr; + int err, irq; + struct net_device *dev; + struct sun4ican_priv *priv; + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "unable to request clock\n"); + err = -ENODEV; + goto exit; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "could not get a valid irq\n"); + err = -ENODEV; + goto exit; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(addr)) { + err = -EBUSY; + goto exit; + } + + dev = alloc_candev(sizeof(struct sun4ican_priv), 1); + if (!dev) { + dev_err(&pdev->dev, + "could not allocate memory for CAN device\n"); + err = -ENOMEM; + goto exit; + } + + dev->netdev_ops = &sun4ican_netdev_ops; + dev->irq = irq; + dev->flags |= IFF_ECHO; + + priv = netdev_priv(dev); + priv->can.clock.freq = clk_get_rate(clk); + priv->can.bittiming_const = &sun4ican_bittiming_const; + priv->can.do_set_mode = sun4ican_set_mode; + priv->can.do_get_berr_counter = sun4ican_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_PRESUME_ACK | + CAN_CTRLMODE_3_SAMPLES; + priv->base = addr; + priv->clk = clk; + spin_lock_init(&priv->cmdreg_lock); + + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_candev(dev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free; + } + devm_can_led_init(dev); + + dev_info(&pdev->dev, "device registered (base=%p, irq=%d)\n", + priv->base, dev->irq); + + return 0; + +exit_free: + free_candev(dev); +exit: + return err; +} + +static struct platform_driver sun4i_can_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = sun4ican_of_match, + }, + .probe = sun4ican_probe, + .remove = sun4ican_remove, +}; + +module_platform_driver(sun4i_can_driver); + +MODULE_AUTHOR("Peter Chen <xingkongcp@gmail.com>"); +MODULE_AUTHOR("Gerhard Bertelsmann <info@gerhard-bertelsmann.de>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION(DRV_NAME "CAN driver for Allwinner SoCs (A10/A20)"); diff --git a/drivers/net/dsa/mv88e6123_61_65.c b/drivers/net/dsa/mv88e6123_61_65.c index 3de2a6d73fdc..4bcfd683bbea 100644 --- a/drivers/net/dsa/mv88e6123_61_65.c +++ b/drivers/net/dsa/mv88e6123_61_65.c @@ -125,7 +125,6 @@ struct dsa_switch_driver mv88e6123_61_65_switch_driver = { .set_addr = mv88e6xxx_set_addr_indirect, .phy_read = mv88e6xxx_phy_read, .phy_write = mv88e6xxx_phy_write, - .poll_link = mv88e6xxx_poll_link, .get_strings = mv88e6xxx_get_strings, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_sset_count = mv88e6xxx_get_sset_count, diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c index 3e8386529965..c73121c8f155 100644 --- a/drivers/net/dsa/mv88e6131.c +++ b/drivers/net/dsa/mv88e6131.c @@ -178,7 +178,6 @@ struct dsa_switch_driver mv88e6131_switch_driver = { .set_addr = mv88e6xxx_set_addr_direct, .phy_read = mv88e6131_phy_read, .phy_write = mv88e6131_phy_write, - .poll_link = mv88e6xxx_poll_link, .get_strings = mv88e6xxx_get_strings, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_sset_count = mv88e6xxx_get_sset_count, diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c index c2daaf087761..ca3330aec740 100644 --- a/drivers/net/dsa/mv88e6171.c +++ b/drivers/net/dsa/mv88e6171.c @@ -104,7 +104,6 @@ struct dsa_switch_driver mv88e6171_switch_driver = { .set_addr = mv88e6xxx_set_addr_indirect, .phy_read = mv88e6xxx_phy_read_indirect, .phy_write = mv88e6xxx_phy_write_indirect, - .poll_link = mv88e6xxx_poll_link, .get_strings = mv88e6xxx_get_strings, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_sset_count = mv88e6xxx_get_sset_count, @@ -122,6 +121,7 @@ struct dsa_switch_driver mv88e6171_switch_driver = { .port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_del = mv88e6xxx_port_vlan_del, .vlan_getnext = mv88e6xxx_vlan_getnext, + .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, .port_fdb_add = mv88e6xxx_port_fdb_add, .port_fdb_del = mv88e6xxx_port_fdb_del, .port_fdb_getnext = mv88e6xxx_port_fdb_getnext, diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c index 1f5129c105fb..078a358c1b83 100644 --- a/drivers/net/dsa/mv88e6352.c +++ b/drivers/net/dsa/mv88e6352.c @@ -324,7 +324,6 @@ struct dsa_switch_driver mv88e6352_switch_driver = { .set_addr = mv88e6xxx_set_addr_indirect, .phy_read = mv88e6xxx_phy_read_indirect, .phy_write = mv88e6xxx_phy_write_indirect, - .poll_link = mv88e6xxx_poll_link, .get_strings = mv88e6xxx_get_strings, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_sset_count = mv88e6xxx_get_sset_count, @@ -349,6 +348,7 @@ struct dsa_switch_driver mv88e6352_switch_driver = { .port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_del = mv88e6xxx_port_vlan_del, .vlan_getnext = mv88e6xxx_vlan_getnext, + .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, .port_fdb_add = mv88e6xxx_port_fdb_add, .port_fdb_del = mv88e6xxx_port_fdb_del, .port_fdb_getnext = mv88e6xxx_port_fdb_getnext, diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 30227ca2d5db..87b405e4f9f6 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -23,6 +23,7 @@ #include <linux/phy.h> #include <linux/seq_file.h> #include <net/dsa.h> +#include <net/switchdev.h> #include "mv88e6xxx.h" /* MDIO bus access can be nested in the case of PHYs connected to the @@ -388,73 +389,6 @@ int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, } #endif -void mv88e6xxx_poll_link(struct dsa_switch *ds) -{ - int i; - - for (i = 0; i < DSA_MAX_PORTS; i++) { - struct net_device *dev; - int uninitialized_var(port_status); - int pcs_ctrl; - int link; - int speed; - int duplex; - int fc; - - dev = ds->ports[i]; - if (dev == NULL) - continue; - - pcs_ctrl = mv88e6xxx_reg_read(ds, REG_PORT(i), PORT_PCS_CTRL); - if (pcs_ctrl < 0 || pcs_ctrl & PORT_PCS_CTRL_FORCE_LINK) - continue; - - link = 0; - if (dev->flags & IFF_UP) { - port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), - PORT_STATUS); - if (port_status < 0) - continue; - - link = !!(port_status & PORT_STATUS_LINK); - } - - if (!link) { - if (netif_carrier_ok(dev)) { - netdev_info(dev, "link down\n"); - netif_carrier_off(dev); - } - continue; - } - - switch (port_status & PORT_STATUS_SPEED_MASK) { - case PORT_STATUS_SPEED_10: - speed = 10; - break; - case PORT_STATUS_SPEED_100: - speed = 100; - break; - case PORT_STATUS_SPEED_1000: - speed = 1000; - break; - default: - speed = -1; - break; - } - 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, - "link up, %d Mb/s, %s duplex, flow control %sabled\n", - speed, - duplex ? "full" : "half", - fc ? "en" : "dis"); - netif_carrier_on(dev); - } - } -} - static bool mv88e6xxx_6065_family(struct dsa_switch *ds) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); @@ -574,7 +508,8 @@ void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - u32 ret, reg; + u32 reg; + int ret; if (!phy_is_pseudo_fixed_link(phydev)) return; @@ -1907,30 +1842,41 @@ static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port, return _mv88e6xxx_atu_load(ds, &entry); } +int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) +{ + /* We don't need any dynamic resource from the kernel (yet), + * so skip the prepare phase. + */ + return 0; +} + int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) { - int state = is_multicast_ether_addr(addr) ? + int state = is_multicast_ether_addr(fdb->addr) ? GLOBAL_ATU_DATA_STATE_MC_STATIC : GLOBAL_ATU_DATA_STATE_UC_STATIC; struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_port_fdb_load(ds, port, addr, vid, state); + ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid, state); mutex_unlock(&ps->smi_mutex); return ret; } int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const struct switchdev_obj_port_fdb *fdb) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_port_fdb_load(ds, port, addr, vid, + ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid, GLOBAL_ATU_DATA_STATE_UNUSED); mutex_unlock(&ps->smi_mutex); @@ -2074,6 +2020,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) */ reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL); if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { + reg &= ~PORT_PCS_CTRL_UNFORCED; reg |= PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP | PORT_PCS_CTRL_DUPLEX_FULL | @@ -2124,6 +2071,8 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA; else reg |= PORT_CONTROL_FRAME_MODE_DSA; + reg |= PORT_CONTROL_FORWARD_UNKNOWN | + PORT_CONTROL_FORWARD_UNKNOWN_MC; } if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 1cf3cf887902..8325c11b9be2 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -442,7 +442,6 @@ 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 port, uint8_t *data); void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data); @@ -475,10 +474,14 @@ int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid, int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid); int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid, unsigned long *ports, unsigned long *untagged); +int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans); int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans); int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); + const struct switchdev_obj_port_fdb *fdb); int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, unsigned char *addr, u16 *vid, bool *is_static); int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg); diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig index edf72258ab1d..29c3075bfb05 100644 --- a/drivers/net/ethernet/8390/Kconfig +++ b/drivers/net/ethernet/8390/Kconfig @@ -64,7 +64,7 @@ config ARM_ETHERH should say Y to this option if you wish to use it with Linux. config MAC8390 - bool "Macintosh NS 8390 based ethernet cards" + tristate "Macintosh NS 8390 based ethernet cards" depends on MAC select CRC32 ---help--- diff --git a/drivers/net/ethernet/8390/mac8390.c b/drivers/net/ethernet/8390/mac8390.c index 65cf60f6718c..b9283901136e 100644 --- a/drivers/net/ethernet/8390/mac8390.c +++ b/drivers/net/ethernet/8390/mac8390.c @@ -454,34 +454,22 @@ MODULE_AUTHOR("David Huggins-Daines <dhd@debian.org> and others"); MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver"); MODULE_LICENSE("GPL"); -/* overkill, of course */ -static struct net_device *dev_mac8390[15]; -int init_module(void) +static struct net_device *dev_mac8390; + +int __init init_module(void) { - int i; - for (i = 0; i < 15; i++) { - struct net_device *dev = mac8390_probe(-1); - if (IS_ERR(dev)) - break; - dev_mac890[i] = dev; - } - if (!i) { - pr_notice("No useable cards found, driver NOT installed.\n"); - return -ENODEV; + dev_mac8390 = mac8390_probe(-1); + if (IS_ERR(dev_mac8390)) { + pr_warn("mac8390: No card found\n"); + return PTR_ERR(dev_mac8390); } return 0; } -void cleanup_module(void) +void __exit cleanup_module(void) { - int i; - for (i = 0; i < 15; i++) { - struct net_device *dev = dev_mac890[i]; - if (dev) { - unregister_netdev(dev); - free_netdev(dev); - } - } + unregister_netdev(dev_mac8390); + free_netdev(dev_mac8390); } #endif /* MODULE */ diff --git a/drivers/net/ethernet/amd/7990.c b/drivers/net/ethernet/amd/7990.c index 98a10d555b79..66d0b73c39c0 100644 --- a/drivers/net/ethernet/amd/7990.c +++ b/drivers/net/ethernet/amd/7990.c @@ -661,6 +661,7 @@ void lance_poll(struct net_device *dev) spin_unlock(&lp->devlock); lance_interrupt(dev->irq, dev); } +EXPORT_SYMBOL_GPL(lance_poll); #endif MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index afc62ea804fc..0038709fd317 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -100,7 +100,7 @@ config DECLANCE DEPCA series. (This chipset is better known via the NE2100 cards.) config HPLANCE - bool "HP on-board LANCE support" + tristate "HP on-board LANCE support" depends on DIO select CRC32 ---help--- diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index a4473d8ff4fa..45512242baea 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1940,84 +1940,31 @@ static void xgbe_config_mtl_mode(struct xgbe_prv_data *pdata) static unsigned int xgbe_calculate_per_queue_fifo(unsigned int fifo_size, unsigned int queue_count) { - unsigned int q_fifo_size = 0; - enum xgbe_mtl_fifo_size p_fifo = XGMAC_MTL_FIFO_SIZE_256; + unsigned int q_fifo_size; + unsigned int p_fifo; - /* Calculate Tx/Rx fifo share per queue */ - switch (fifo_size) { - case 0: - q_fifo_size = XGBE_FIFO_SIZE_B(128); - break; - case 1: - q_fifo_size = XGBE_FIFO_SIZE_B(256); - break; - case 2: - q_fifo_size = XGBE_FIFO_SIZE_B(512); - break; - case 3: - q_fifo_size = XGBE_FIFO_SIZE_KB(1); - break; - case 4: - q_fifo_size = XGBE_FIFO_SIZE_KB(2); - break; - case 5: - q_fifo_size = XGBE_FIFO_SIZE_KB(4); - break; - case 6: - q_fifo_size = XGBE_FIFO_SIZE_KB(8); - break; - case 7: - q_fifo_size = XGBE_FIFO_SIZE_KB(16); - break; - case 8: - q_fifo_size = XGBE_FIFO_SIZE_KB(32); - break; - case 9: - q_fifo_size = XGBE_FIFO_SIZE_KB(64); - break; - case 10: - q_fifo_size = XGBE_FIFO_SIZE_KB(128); - break; - case 11: - q_fifo_size = XGBE_FIFO_SIZE_KB(256); - break; - } + /* Calculate the configured fifo size */ + q_fifo_size = 1 << (fifo_size + 7); - /* The configured value is not the actual amount of fifo RAM */ + /* The configured value may not be the actual amount of fifo RAM */ q_fifo_size = min_t(unsigned int, XGBE_FIFO_MAX, q_fifo_size); q_fifo_size = q_fifo_size / queue_count; - /* Set the queue fifo size programmable value */ - if (q_fifo_size >= XGBE_FIFO_SIZE_KB(256)) - p_fifo = XGMAC_MTL_FIFO_SIZE_256K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(128)) - p_fifo = XGMAC_MTL_FIFO_SIZE_128K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(64)) - p_fifo = XGMAC_MTL_FIFO_SIZE_64K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(32)) - p_fifo = XGMAC_MTL_FIFO_SIZE_32K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(16)) - p_fifo = XGMAC_MTL_FIFO_SIZE_16K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(8)) - p_fifo = XGMAC_MTL_FIFO_SIZE_8K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(4)) - p_fifo = XGMAC_MTL_FIFO_SIZE_4K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(2)) - p_fifo = XGMAC_MTL_FIFO_SIZE_2K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(1)) - p_fifo = XGMAC_MTL_FIFO_SIZE_1K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_B(512)) - p_fifo = XGMAC_MTL_FIFO_SIZE_512; - else if (q_fifo_size >= XGBE_FIFO_SIZE_B(256)) - p_fifo = XGMAC_MTL_FIFO_SIZE_256; + /* Each increment in the queue fifo size represents 256 bytes of + * fifo, with 0 representing 256 bytes. Distribute the fifo equally + * between the queues. + */ + p_fifo = q_fifo_size / 256; + if (p_fifo) + p_fifo--; return p_fifo; } static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata) { - enum xgbe_mtl_fifo_size fifo_size; + unsigned int fifo_size; unsigned int i; fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size, @@ -2033,7 +1980,7 @@ static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata) static void xgbe_config_rx_fifo_size(struct xgbe_prv_data *pdata) { - enum xgbe_mtl_fifo_size fifo_size; + unsigned int fifo_size; unsigned int i; fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.rx_fifo_size, @@ -2224,7 +2171,7 @@ static u64 xgbe_mmc_read(struct xgbe_prv_data *pdata, unsigned int reg_lo) default: read_hi = false; - }; + } val = XGMAC_IOREAD(pdata, reg_lo); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index aae9d5ecd182..14bad8c44c87 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -360,9 +360,12 @@ static irqreturn_t xgbe_isr(int irq, void *data) } } + if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RBU)) + pdata->ext_stats.rx_buffer_unavailable++; + /* Restart the device on a Fatal Bus Error */ if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE)) - schedule_work(&pdata->restart_work); + queue_work(pdata->dev_workqueue, &pdata->restart_work); /* Clear all interrupt signals */ XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_ch_isr); @@ -384,7 +387,8 @@ static irqreturn_t xgbe_isr(int irq, void *data) /* Read Tx Timestamp to clear interrupt */ pdata->tx_tstamp = hw_if->get_tx_tstamp(pdata); - schedule_work(&pdata->tx_tstamp_work); + queue_work(pdata->dev_workqueue, + &pdata->tx_tstamp_work); } } } @@ -450,7 +454,7 @@ static void xgbe_service_timer(unsigned long data) { struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; - schedule_work(&pdata->service_work); + queue_work(pdata->dev_workqueue, &pdata->service_work); mod_timer(&pdata->service_timer, jiffies + HZ); } @@ -891,7 +895,7 @@ static int xgbe_start(struct xgbe_prv_data *pdata) netif_tx_start_all_queues(netdev); xgbe_start_timers(pdata); - schedule_work(&pdata->service_work); + queue_work(pdata->dev_workqueue, &pdata->service_work); DBGPR("<--xgbe_start\n"); @@ -1533,7 +1537,7 @@ static void xgbe_tx_timeout(struct net_device *netdev) struct xgbe_prv_data *pdata = netdev_priv(netdev); netdev_warn(netdev, "tx timeout, device restarting\n"); - schedule_work(&pdata->restart_work); + queue_work(pdata->dev_workqueue, &pdata->restart_work); } static struct rtnl_link_stats64 *xgbe_get_stats64(struct net_device *netdev, diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 59e090e95c0e..204fb3afb182 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -179,6 +179,7 @@ static const struct xgbe_stats xgbe_gstring_stats[] = { XGMAC_MMC_STAT("rx_watchdog_errors", rxwatchdogerror), XGMAC_MMC_STAT("rx_pause_frames", rxpauseframes), XGMAC_EXT_STAT("rx_split_header_packets", rx_split_header_packets), + XGMAC_EXT_STAT("rx_buffer_unavailable", rx_buffer_unavailable), }; #define XGBE_STATS_COUNT ARRAY_SIZE(xgbe_gstring_stats) @@ -187,8 +188,6 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { int i; - DBGPR("-->%s\n", __func__); - switch (stringset) { case ETH_SS_STATS: for (i = 0; i < XGBE_STATS_COUNT; i++) { @@ -198,8 +197,6 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) } break; } - - DBGPR("<--%s\n", __func__); } static void xgbe_get_ethtool_stats(struct net_device *netdev, @@ -209,23 +206,17 @@ static void xgbe_get_ethtool_stats(struct net_device *netdev, u8 *stat; int i; - DBGPR("-->%s\n", __func__); - pdata->hw_if.read_mmc_stats(pdata); for (i = 0; i < XGBE_STATS_COUNT; i++) { stat = (u8 *)pdata + xgbe_gstring_stats[i].stat_offset; *data++ = *(u64 *)stat; } - - DBGPR("<--%s\n", __func__); } static int xgbe_get_sset_count(struct net_device *netdev, int stringset) { int ret; - DBGPR("-->%s\n", __func__); - switch (stringset) { case ETH_SS_STATS: ret = XGBE_STATS_COUNT; @@ -235,8 +226,6 @@ static int xgbe_get_sset_count(struct net_device *netdev, int stringset) ret = -EOPNOTSUPP; } - DBGPR("<--%s\n", __func__); - return ret; } @@ -245,13 +234,9 @@ static void xgbe_get_pauseparam(struct net_device *netdev, { struct xgbe_prv_data *pdata = netdev_priv(netdev); - DBGPR("-->xgbe_get_pauseparam\n"); - pause->autoneg = pdata->phy.pause_autoneg; pause->tx_pause = pdata->phy.tx_pause; pause->rx_pause = pdata->phy.rx_pause; - - DBGPR("<--xgbe_get_pauseparam\n"); } static int xgbe_set_pauseparam(struct net_device *netdev, @@ -260,13 +245,11 @@ static int xgbe_set_pauseparam(struct net_device *netdev, struct xgbe_prv_data *pdata = netdev_priv(netdev); int ret = 0; - DBGPR("-->xgbe_set_pauseparam\n"); - - DBGPR(" autoneg = %d, tx_pause = %d, rx_pause = %d\n", - pause->autoneg, pause->tx_pause, pause->rx_pause); - - if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE)) + if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE)) { + netdev_err(netdev, + "autoneg disabled, pause autoneg not avialable\n"); return -EINVAL; + } pdata->phy.pause_autoneg = pause->autoneg; pdata->phy.tx_pause = pause->tx_pause; @@ -286,8 +269,6 @@ static int xgbe_set_pauseparam(struct net_device *netdev, if (netif_running(netdev)) ret = pdata->phy_if.phy_config_aneg(pdata); - DBGPR("<--xgbe_set_pauseparam\n"); - return ret; } @@ -296,8 +277,6 @@ static int xgbe_get_settings(struct net_device *netdev, { struct xgbe_prv_data *pdata = netdev_priv(netdev); - DBGPR("-->xgbe_get_settings\n"); - cmd->phy_address = pdata->phy.address; cmd->supported = pdata->phy.supported; @@ -311,8 +290,6 @@ static int xgbe_get_settings(struct net_device *netdev, cmd->port = PORT_NONE; cmd->transceiver = XCVR_INTERNAL; - DBGPR("<--xgbe_get_settings\n"); - return 0; } @@ -323,16 +300,20 @@ static int xgbe_set_settings(struct net_device *netdev, u32 speed; int ret; - DBGPR("-->xgbe_set_settings\n"); - speed = ethtool_cmd_speed(cmd); - if (cmd->phy_address != pdata->phy.address) + if (cmd->phy_address != pdata->phy.address) { + netdev_err(netdev, "invalid phy address %hhu\n", + cmd->phy_address); return -EINVAL; + } if ((cmd->autoneg != AUTONEG_ENABLE) && - (cmd->autoneg != AUTONEG_DISABLE)) + (cmd->autoneg != AUTONEG_DISABLE)) { + netdev_err(netdev, "unsupported autoneg %hhu\n", + cmd->autoneg); return -EINVAL; + } if (cmd->autoneg == AUTONEG_DISABLE) { switch (speed) { @@ -341,16 +322,27 @@ static int xgbe_set_settings(struct net_device *netdev, case SPEED_1000: break; default: + netdev_err(netdev, "unsupported speed %u\n", speed); return -EINVAL; } - if (cmd->duplex != DUPLEX_FULL) + if (cmd->duplex != DUPLEX_FULL) { + netdev_err(netdev, "unsupported duplex %hhu\n", + cmd->duplex); return -EINVAL; + } } + netif_dbg(pdata, link, netdev, + "requested advertisement %#x, phy supported %#x\n", + cmd->advertising, pdata->phy.supported); + cmd->advertising &= pdata->phy.supported; - if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising) + if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising) { + netdev_err(netdev, + "unsupported requested advertisement\n"); return -EINVAL; + } ret = 0; pdata->phy.autoneg = cmd->autoneg; @@ -366,8 +358,6 @@ static int xgbe_set_settings(struct net_device *netdev, if (netif_running(netdev)) ret = pdata->phy_if.phy_config_aneg(pdata); - DBGPR("<--xgbe_set_settings\n"); - return ret; } @@ -388,13 +378,25 @@ static void xgbe_get_drvinfo(struct net_device *netdev, drvinfo->n_stats = XGBE_STATS_COUNT; } +static u32 xgbe_get_msglevel(struct net_device *netdev) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + return pdata->msg_enable; +} + +static void xgbe_set_msglevel(struct net_device *netdev, u32 msglevel) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + pdata->msg_enable = msglevel; +} + static int xgbe_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) { struct xgbe_prv_data *pdata = netdev_priv(netdev); - DBGPR("-->xgbe_get_coalesce\n"); - memset(ec, 0, sizeof(struct ethtool_coalesce)); ec->rx_coalesce_usecs = pdata->rx_usecs; @@ -402,8 +404,6 @@ static int xgbe_get_coalesce(struct net_device *netdev, ec->tx_max_coalesced_frames = pdata->tx_frames; - DBGPR("<--xgbe_get_coalesce\n"); - return 0; } @@ -415,8 +415,6 @@ static int xgbe_set_coalesce(struct net_device *netdev, unsigned int rx_frames, rx_riwt, rx_usecs; unsigned int tx_frames; - DBGPR("-->xgbe_set_coalesce\n"); - /* Check for not supported parameters */ if ((ec->rx_coalesce_usecs_irq) || (ec->rx_max_coalesced_frames_irq) || @@ -436,8 +434,10 @@ static int xgbe_set_coalesce(struct net_device *netdev, (ec->rx_max_coalesced_frames_high) || (ec->tx_coalesce_usecs_high) || (ec->tx_max_coalesced_frames_high) || - (ec->rate_sample_interval)) + (ec->rate_sample_interval)) { + netdev_err(netdev, "unsupported coalescing parameter\n"); return -EOPNOTSUPP; + } rx_riwt = hw_if->usec_to_riwt(pdata, ec->rx_coalesce_usecs); rx_usecs = ec->rx_coalesce_usecs; @@ -449,13 +449,13 @@ static int xgbe_set_coalesce(struct net_device *netdev, /* Check the bounds of values for Rx */ if (rx_riwt > XGMAC_MAX_DMA_RIWT) { - netdev_alert(netdev, "rx-usec is limited to %d usecs\n", - hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT)); + netdev_err(netdev, "rx-usec is limited to %d usecs\n", + hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT)); return -EINVAL; } if (rx_frames > pdata->rx_desc_count) { - netdev_alert(netdev, "rx-frames is limited to %d frames\n", - pdata->rx_desc_count); + netdev_err(netdev, "rx-frames is limited to %d frames\n", + pdata->rx_desc_count); return -EINVAL; } @@ -463,8 +463,8 @@ static int xgbe_set_coalesce(struct net_device *netdev, /* Check the bounds of values for Tx */ if (tx_frames > pdata->tx_desc_count) { - netdev_alert(netdev, "tx-frames is limited to %d frames\n", - pdata->tx_desc_count); + netdev_err(netdev, "tx-frames is limited to %d frames\n", + pdata->tx_desc_count); return -EINVAL; } @@ -476,8 +476,6 @@ static int xgbe_set_coalesce(struct net_device *netdev, pdata->tx_frames = tx_frames; hw_if->config_tx_coalesce(pdata); - DBGPR("<--xgbe_set_coalesce\n"); - return 0; } @@ -539,8 +537,10 @@ static int xgbe_set_rxfh(struct net_device *netdev, const u32 *indir, struct xgbe_hw_if *hw_if = &pdata->hw_if; unsigned int ret; - if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) + if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) { + netdev_err(netdev, "unsupported hash function\n"); return -EOPNOTSUPP; + } if (indir) { ret = hw_if->set_rss_lookup_table(pdata, indir); @@ -594,6 +594,8 @@ static const struct ethtool_ops xgbe_ethtool_ops = { .get_settings = xgbe_get_settings, .set_settings = xgbe_set_settings, .get_drvinfo = xgbe_get_drvinfo, + .get_msglevel = xgbe_get_msglevel, + .set_msglevel = xgbe_set_msglevel, .get_link = ethtool_op_get_link, .get_coalesce = xgbe_get_coalesce, .set_coalesce = xgbe_set_coalesce, diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index e83bd76abce6..7dd893331785 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -371,7 +371,7 @@ static int xgbe_probe(struct platform_device *pdev) set_bit(XGBE_DOWN, &pdata->dev_state); /* Check if we should use ACPI or DT */ - pdata->use_acpi = (!pdata->adev || acpi_disabled) ? 0 : 1; + pdata->use_acpi = dev->of_node ? 0 : 1; phy_pdev = xgbe_get_phy_pdev(pdata); if (!phy_pdev) { diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 9088c3a35a20..446058081866 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -1115,8 +1115,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) unsigned int reg, link_aneg; if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) { - if (test_and_clear_bit(XGBE_LINK, &pdata->dev_state)) - netif_carrier_off(pdata->netdev); + netif_carrier_off(pdata->netdev); pdata->phy.link = 0; goto adjust_link; @@ -1142,10 +1141,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) clear_bit(XGBE_LINK_INIT, &pdata->dev_state); - if (!test_bit(XGBE_LINK, &pdata->dev_state)) { - set_bit(XGBE_LINK, &pdata->dev_state); - netif_carrier_on(pdata->netdev); - } + netif_carrier_on(pdata->netdev); } else { if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) { xgbe_check_link_timeout(pdata); @@ -1156,10 +1152,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) xgbe_phy_status_aneg(pdata); - if (test_bit(XGBE_LINK, &pdata->dev_state)) { - clear_bit(XGBE_LINK, &pdata->dev_state); - netif_carrier_off(pdata->netdev); - } + netif_carrier_off(pdata->netdev); } adjust_link: @@ -1179,8 +1172,7 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata) devm_free_irq(pdata->dev, pdata->an_irq, pdata); pdata->phy.link = 0; - if (test_and_clear_bit(XGBE_LINK, &pdata->dev_state)) - netif_carrier_off(pdata->netdev); + netif_carrier_off(pdata->netdev); xgbe_phy_adjust_link(pdata); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 8c9d01ef730d..e234b9970318 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -209,8 +209,6 @@ #define XGMAC_IOCTL_CONTEXT 2 #define XGBE_FIFO_MAX 81920 -#define XGBE_FIFO_SIZE_B(x) (x) -#define XGBE_FIFO_SIZE_KB(x) (x * 1024) #define XGBE_TC_MIN_QUANTUM 10 @@ -461,7 +459,6 @@ struct xgbe_channel { enum xgbe_state { XGBE_DOWN, - XGBE_LINK, XGBE_LINK_INIT, XGBE_LINK_ERR, }; @@ -483,20 +480,6 @@ enum xgbe_int_state { XGMAC_INT_STATE_RESTORE, }; -enum xgbe_mtl_fifo_size { - XGMAC_MTL_FIFO_SIZE_256 = 0x00, - XGMAC_MTL_FIFO_SIZE_512 = 0x01, - XGMAC_MTL_FIFO_SIZE_1K = 0x03, - XGMAC_MTL_FIFO_SIZE_2K = 0x07, - XGMAC_MTL_FIFO_SIZE_4K = 0x0f, - XGMAC_MTL_FIFO_SIZE_8K = 0x1f, - XGMAC_MTL_FIFO_SIZE_16K = 0x3f, - XGMAC_MTL_FIFO_SIZE_32K = 0x7f, - XGMAC_MTL_FIFO_SIZE_64K = 0xff, - XGMAC_MTL_FIFO_SIZE_128K = 0x1ff, - XGMAC_MTL_FIFO_SIZE_256K = 0x3ff, -}; - enum xgbe_speed { XGBE_SPEED_1000 = 0, XGBE_SPEED_2500, @@ -598,6 +581,7 @@ struct xgbe_mmc_stats { struct xgbe_ext_stats { u64 tx_tso_packets; u64 rx_split_header_packets; + u64 rx_buffer_unavailable; }; struct xgbe_hw_if { diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 21749f01827d..652f21889a48 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -690,16 +690,24 @@ static int xgene_enet_phy_connect(struct net_device *ndev) netdev_dbg(ndev, "No phy-handle found in DT\n"); return -ENODEV; } - pdata->phy_dev = of_phy_find_device(phy_np); - } - phy_dev = pdata->phy_dev; + phy_dev = of_phy_connect(ndev, phy_np, &xgene_enet_adjust_link, + 0, pdata->phy_mode); + if (!phy_dev) { + netdev_err(ndev, "Could not connect to PHY\n"); + return -ENODEV; + } + + pdata->phy_dev = phy_dev; + } else { + phy_dev = pdata->phy_dev; - if (!phy_dev || - phy_connect_direct(ndev, phy_dev, &xgene_enet_adjust_link, - pdata->phy_mode)) { - netdev_err(ndev, "Could not connect to PHY\n"); - return -ENODEV; + if (!phy_dev || + phy_connect_direct(ndev, phy_dev, &xgene_enet_adjust_link, + pdata->phy_mode)) { + netdev_err(ndev, "Could not connect to PHY\n"); + return -ENODEV; + } } pdata->phy_speed = SPEED_UNKNOWN; diff --git a/drivers/net/ethernet/apple/Kconfig b/drivers/net/ethernet/apple/Kconfig index d19a41b0c6d2..31071297896c 100644 --- a/drivers/net/ethernet/apple/Kconfig +++ b/drivers/net/ethernet/apple/Kconfig @@ -51,7 +51,7 @@ config BMAC will be called bmac. config MACMACE - bool "Macintosh (AV) onboard MACE ethernet" + tristate "Macintosh (AV) onboard MACE ethernet" depends on MAC select CRC32 ---help--- diff --git a/drivers/net/ethernet/arc/emac_arc.c b/drivers/net/ethernet/arc/emac_arc.c index f9cb99bfb511..ffd180570920 100644 --- a/drivers/net/ethernet/arc/emac_arc.c +++ b/drivers/net/ethernet/arc/emac_arc.c @@ -78,6 +78,7 @@ static const struct of_device_id emac_arc_dt_ids[] = { { .compatible = "snps,arc-emac" }, { /* Sentinel */ } }; +MODULE_DEVICE_TABLE(of, emac_arc_dt_ids); static struct platform_driver emac_arc_driver = { .probe = emac_arc_probe, diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index b9a5a97ed4dd..f1b5364f3521 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -2079,6 +2079,7 @@ static const struct of_device_id bcm_sysport_of_match[] = { { .compatible = "brcm,systemport" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, bcm_sysport_of_match); static struct platform_driver bcm_sysport_driver = { .probe = bcm_sysport_probe, diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 2b66ef3d8217..8fc3f3c137f8 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -813,6 +813,46 @@ bnx2_alloc_rx_mem(struct bnx2 *bp) } static void +bnx2_free_stats_blk(struct net_device *dev) +{ + struct bnx2 *bp = netdev_priv(dev); + + if (bp->status_blk) { + dma_free_coherent(&bp->pdev->dev, bp->status_stats_size, + bp->status_blk, + bp->status_blk_mapping); + bp->status_blk = NULL; + bp->stats_blk = NULL; + } +} + +static int +bnx2_alloc_stats_blk(struct net_device *dev) +{ + int status_blk_size; + void *status_blk; + struct bnx2 *bp = netdev_priv(dev); + + /* Combine status and statistics blocks into one allocation. */ + status_blk_size = L1_CACHE_ALIGN(sizeof(struct status_block)); + if (bp->flags & BNX2_FLAG_MSIX_CAP) + status_blk_size = L1_CACHE_ALIGN(BNX2_MAX_MSIX_HW_VEC * + BNX2_SBLK_MSIX_ALIGN_SIZE); + bp->status_stats_size = status_blk_size + + sizeof(struct statistics_block); + status_blk = dma_zalloc_coherent(&bp->pdev->dev, bp->status_stats_size, + &bp->status_blk_mapping, GFP_KERNEL); + if (status_blk == NULL) + return -ENOMEM; + + bp->status_blk = status_blk; + bp->stats_blk = status_blk + status_blk_size; + bp->stats_blk_mapping = bp->status_blk_mapping + status_blk_size; + + return 0; +} + +static void bnx2_free_mem(struct bnx2 *bp) { int i; @@ -829,37 +869,19 @@ bnx2_free_mem(struct bnx2 *bp) bp->ctx_blk[i] = NULL; } } - if (bnapi->status_blk.msi) { - dma_free_coherent(&bp->pdev->dev, bp->status_stats_size, - bnapi->status_blk.msi, - bp->status_blk_mapping); + + if (bnapi->status_blk.msi) bnapi->status_blk.msi = NULL; - bp->stats_blk = NULL; - } } static int bnx2_alloc_mem(struct bnx2 *bp) { - int i, status_blk_size, err; + int i, err; struct bnx2_napi *bnapi; - void *status_blk; - - /* Combine status and statistics blocks into one allocation. */ - status_blk_size = L1_CACHE_ALIGN(sizeof(struct status_block)); - if (bp->flags & BNX2_FLAG_MSIX_CAP) - status_blk_size = L1_CACHE_ALIGN(BNX2_MAX_MSIX_HW_VEC * - BNX2_SBLK_MSIX_ALIGN_SIZE); - bp->status_stats_size = status_blk_size + - sizeof(struct statistics_block); - - status_blk = dma_zalloc_coherent(&bp->pdev->dev, bp->status_stats_size, - &bp->status_blk_mapping, GFP_KERNEL); - if (status_blk == NULL) - goto alloc_mem_err; bnapi = &bp->bnx2_napi[0]; - bnapi->status_blk.msi = status_blk; + bnapi->status_blk.msi = bp->status_blk; bnapi->hw_tx_cons_ptr = &bnapi->status_blk.msi->status_tx_quick_consumer_index0; bnapi->hw_rx_cons_ptr = @@ -870,7 +892,7 @@ bnx2_alloc_mem(struct bnx2 *bp) bnapi = &bp->bnx2_napi[i]; - sblk = (status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i); + sblk = (bp->status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i); bnapi->status_blk.msix = sblk; bnapi->hw_tx_cons_ptr = &sblk->status_tx_quick_consumer_index; @@ -880,10 +902,6 @@ bnx2_alloc_mem(struct bnx2 *bp) } } - bp->stats_blk = status_blk + status_blk_size; - - bp->stats_blk_mapping = bp->status_blk_mapping + status_blk_size; - if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { bp->ctx_pages = 0x2000 / BNX2_PAGE_SIZE; if (bp->ctx_pages == 0) @@ -8330,6 +8348,11 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->phy_addr = 1; + /* allocate stats_blk */ + rc = bnx2_alloc_stats_blk(dev); + if (rc) + goto err_out_unmap; + /* Disable WOL support if we are running on a SERDES chip. */ if (BNX2_CHIP(bp) == BNX2_CHIP_5709) bnx2_get_5709_media(bp); @@ -8453,6 +8476,8 @@ err_out_disable: pci_disable_device(pdev); err_out: + kfree(bp->temp_stats_blk); + return rc; } @@ -8586,6 +8611,7 @@ error: pci_release_regions(pdev); pci_disable_device(pdev); err_free: + bnx2_free_stats_blk(dev); free_netdev(dev); return rc; } @@ -8603,6 +8629,7 @@ bnx2_remove_one(struct pci_dev *pdev) pci_iounmap(bp->pdev, bp->regview); + bnx2_free_stats_blk(dev); kfree(bp->temp_stats_blk); if (bp->flags & BNX2_FLAG_AER_ENABLED) { diff --git a/drivers/net/ethernet/broadcom/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h index f92f76c44756..380234d72b95 100644 --- a/drivers/net/ethernet/broadcom/bnx2.h +++ b/drivers/net/ethernet/broadcom/bnx2.h @@ -6928,6 +6928,7 @@ struct bnx2 { dma_addr_t status_blk_mapping; + void *status_blk; struct statistics_block *stats_blk; struct statistics_block *temp_stats_blk; dma_addr_t stats_blk_mapping; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index ba936635322a..b5e64b02200c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1946,6 +1946,7 @@ struct bnx2x { u16 vlan_cnt; u16 vlan_credit; u16 vxlan_dst_port; + u8 vxlan_dst_port_count; bool accept_any_vlan; }; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index e3da2bddf143..f1d62d5dbaff 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -3705,16 +3705,14 @@ out: void bnx2x_update_mfw_dump(struct bnx2x *bp) { - struct timeval epoc; u32 drv_ver; u32 valid_dump; if (!SHMEM2_HAS(bp, drv_info)) return; - /* Update Driver load time */ - do_gettimeofday(&epoc); - SHMEM2_WR(bp, drv_info.epoc, epoc.tv_sec); + /* Update Driver load time, possibly broken in y2038 */ + SHMEM2_WR(bp, drv_info.epoc, (u32)ktime_get_real_seconds()); drv_ver = bnx2x_update_mng_version_utility(DRV_MODULE_VERSION, true); SHMEM2_WR(bp, drv_info.drv_ver, drv_ver); @@ -10110,12 +10108,18 @@ static void __bnx2x_add_vxlan_port(struct bnx2x *bp, u16 port) if (!netif_running(bp->dev)) return; - if (bp->vxlan_dst_port || !IS_PF(bp)) { + if (bp->vxlan_dst_port_count && bp->vxlan_dst_port == port) { + bp->vxlan_dst_port_count++; + return; + } + + if (bp->vxlan_dst_port_count || !IS_PF(bp)) { DP(BNX2X_MSG_SP, "Vxlan destination port limit reached\n"); return; } bp->vxlan_dst_port = port; + bp->vxlan_dst_port_count = 1; bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_ADD_VXLAN_PORT, 0); } @@ -10130,10 +10134,14 @@ static void bnx2x_add_vxlan_port(struct net_device *netdev, static void __bnx2x_del_vxlan_port(struct bnx2x *bp, u16 port) { - if (!bp->vxlan_dst_port || bp->vxlan_dst_port != port || !IS_PF(bp)) { + if (!bp->vxlan_dst_port_count || bp->vxlan_dst_port != port || + !IS_PF(bp)) { DP(BNX2X_MSG_SP, "Invalid vxlan port\n"); return; } + bp->vxlan_dst_port--; + if (bp->vxlan_dst_port) + return; if (netif_running(bp->dev)) { bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_DEL_VXLAN_PORT, 0); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c index c9bd7f16018e..ff702a707a91 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c @@ -4319,8 +4319,16 @@ static int bnx2x_setup_rss(struct bnx2x *bp, /* RSS keys */ if (test_bit(BNX2X_RSS_SET_SRCH, &p->rss_flags)) { - memcpy(&data->rss_key[0], &p->rss_key[0], - sizeof(data->rss_key)); + u8 *dst = (u8 *)(data->rss_key) + sizeof(data->rss_key); + const u8 *src = (const u8 *)p->rss_key; + int i; + + /* Apparently, bnx2x reads this array in reverse order + * We need to byte swap rss_key to comply with Toeplitz specs. + */ + for (i = 0; i < sizeof(data->rss_key); i++) + *--dst = *src++; + caps |= ETH_RSS_UPDATE_RAMROD_DATA_UPDATE_RSS_KEY; } diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 1e72722abbf1..1a3988f51305 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -608,8 +608,7 @@ static int bcmgenet_set_coalesce(struct net_device *dev, * transmitted, or when the ring is emtpy. */ if (ec->tx_coalesce_usecs || ec->tx_coalesce_usecs_high || - ec->tx_coalesce_usecs_irq || ec->tx_coalesce_usecs_high || - ec->tx_coalesce_usecs_low) + ec->tx_coalesce_usecs_irq || ec->tx_coalesce_usecs_low) return -EOPNOTSUPP; /* Program all TX queues with the same values, as there is no @@ -3305,6 +3304,7 @@ static const struct of_device_id bcmgenet_match[] = { { .compatible = "brcm,genet-v4", .data = (void *)GENET_V4 }, { }, }; +MODULE_DEVICE_TABLE(of, bcmgenet_match); static int bcmgenet_probe(struct platform_device *pdev) { diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.c b/drivers/net/ethernet/brocade/bna/bfa_ioc.c index b7a0f7879de2..9e59663a6ead 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.c +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c @@ -1543,7 +1543,7 @@ bfa_flash_cmd_act_check(void __iomem *pci_bar) } /* Flush FLI data fifo. */ -static u32 +static int bfa_flash_fifo_flush(void __iomem *pci_bar) { u32 i; @@ -1573,11 +1573,11 @@ bfa_flash_fifo_flush(void __iomem *pci_bar) } /* Read flash status. */ -static u32 +static int bfa_flash_status_read(void __iomem *pci_bar) { union bfa_flash_dev_status_reg dev_status; - u32 status; + int status; u32 ret_status; int i; @@ -1611,11 +1611,11 @@ bfa_flash_status_read(void __iomem *pci_bar) } /* Start flash read operation. */ -static u32 +static int bfa_flash_read_start(void __iomem *pci_bar, u32 offset, u32 len, char *buf) { - u32 status; + int status; /* len must be mutiple of 4 and not exceeding fifo size */ if (len == 0 || len > BFA_FLASH_FIFO_SIZE || (len & 0x03) != 0) @@ -1703,7 +1703,8 @@ static enum bfa_status bfa_flash_raw_read(void __iomem *pci_bar, u32 offset, char *buf, u32 len) { - u32 n, status; + u32 n; + int status; u32 off, l, s, residue, fifo_sz; residue = len; diff --git a/drivers/net/ethernet/brocade/bna/bna_tx_rx.c b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c index 5d0753cc7e73..04b0d16b210e 100644 --- a/drivers/net/ethernet/brocade/bna/bna_tx_rx.c +++ b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c @@ -2400,6 +2400,7 @@ bna_rx_create(struct bna *bna, struct bnad *bnad, q0->rcb->id = 0; q0->rx_packets = q0->rx_bytes = 0; q0->rx_packets_with_error = q0->rxbuf_alloc_failed = 0; + q0->rxbuf_map_failed = 0; bna_rxq_qpt_setup(q0, rxp, dpage_count, PAGE_SIZE, &dqpt_mem[i], &dsqpt_mem[i], &dpage_mem[i]); @@ -2428,6 +2429,7 @@ bna_rx_create(struct bna *bna, struct bnad *bnad, : rx_cfg->q1_buf_size; q1->rx_packets = q1->rx_bytes = 0; q1->rx_packets_with_error = q1->rxbuf_alloc_failed = 0; + q1->rxbuf_map_failed = 0; bna_rxq_qpt_setup(q1, rxp, hpage_count, PAGE_SIZE, &hqpt_mem[i], &hsqpt_mem[i], diff --git a/drivers/net/ethernet/brocade/bna/bna_types.h b/drivers/net/ethernet/brocade/bna/bna_types.h index e0e797f2ea14..c438d032e8bf 100644 --- a/drivers/net/ethernet/brocade/bna/bna_types.h +++ b/drivers/net/ethernet/brocade/bna/bna_types.h @@ -587,6 +587,7 @@ struct bna_rxq { u64 rx_bytes; u64 rx_packets_with_error; u64 rxbuf_alloc_failed; + u64 rxbuf_map_failed; }; /* RxQ pair */ diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index 506047c38607..21a0cfc3e7ec 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -399,7 +399,13 @@ bnad_rxq_refill_page(struct bnad *bnad, struct bna_rcb *rcb, u32 nalloc) } dma_addr = dma_map_page(&bnad->pcidev->dev, page, page_offset, - unmap_q->map_size, DMA_FROM_DEVICE); + unmap_q->map_size, DMA_FROM_DEVICE); + if (dma_mapping_error(&bnad->pcidev->dev, dma_addr)) { + put_page(page); + BNAD_UPDATE_CTR(bnad, rxbuf_map_failed); + rcb->rxq->rxbuf_map_failed++; + goto finishing; + } unmap->page = page; unmap->page_offset = page_offset; @@ -454,8 +460,15 @@ bnad_rxq_refill_skb(struct bnad *bnad, struct bna_rcb *rcb, u32 nalloc) rcb->rxq->rxbuf_alloc_failed++; goto finishing; } + dma_addr = dma_map_single(&bnad->pcidev->dev, skb->data, buff_sz, DMA_FROM_DEVICE); + if (dma_mapping_error(&bnad->pcidev->dev, dma_addr)) { + dev_kfree_skb_any(skb); + BNAD_UPDATE_CTR(bnad, rxbuf_map_failed); + rcb->rxq->rxbuf_map_failed++; + goto finishing; + } unmap->skb = skb; dma_unmap_addr_set(&unmap->vector, dma_addr, dma_addr); @@ -3025,6 +3038,11 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev) unmap = head_unmap; dma_addr = dma_map_single(&bnad->pcidev->dev, skb->data, len, DMA_TO_DEVICE); + if (dma_mapping_error(&bnad->pcidev->dev, dma_addr)) { + dev_kfree_skb_any(skb); + BNAD_UPDATE_CTR(bnad, tx_skb_map_failed); + return NETDEV_TX_OK; + } BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[0].host_addr); txqent->vector[0].length = htons(len); dma_unmap_addr_set(&unmap->vectors[0], dma_addr, dma_addr); @@ -3056,6 +3074,15 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev) dma_addr = skb_frag_dma_map(&bnad->pcidev->dev, frag, 0, size, DMA_TO_DEVICE); + if (dma_mapping_error(&bnad->pcidev->dev, dma_addr)) { + /* Undo the changes starting at tcb->producer_index */ + bnad_tx_buff_unmap(bnad, unmap_q, q_depth, + tcb->producer_index); + dev_kfree_skb_any(skb); + BNAD_UPDATE_CTR(bnad, tx_skb_map_failed); + return NETDEV_TX_OK; + } + dma_unmap_len_set(&unmap->vectors[vect_id], dma_len, size); BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[vect_id].host_addr); txqent->vector[vect_id].length = htons(size); diff --git a/drivers/net/ethernet/brocade/bna/bnad.h b/drivers/net/ethernet/brocade/bna/bnad.h index faedbf24777e..f4ed816b93ee 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.h +++ b/drivers/net/ethernet/brocade/bna/bnad.h @@ -175,6 +175,7 @@ struct bnad_drv_stats { u64 tx_skb_headlen_zero; u64 tx_skb_frag_zero; u64 tx_skb_len_mismatch; + u64 tx_skb_map_failed; u64 hw_stats_updates; u64 netif_rx_dropped; @@ -189,6 +190,7 @@ struct bnad_drv_stats { u64 rx_unmap_q_alloc_failed; u64 rxbuf_alloc_failed; + u64 rxbuf_map_failed; }; /* Complete driver stats */ diff --git a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c index 2bdfc5dff4b1..0e4fdc3dd729 100644 --- a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c +++ b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c @@ -90,6 +90,7 @@ static const char *bnad_net_stats_strings[BNAD_ETHTOOL_STATS_NUM] = { "tx_skb_headlen_zero", "tx_skb_frag_zero", "tx_skb_len_mismatch", + "tx_skb_map_failed", "hw_stats_updates", "netif_rx_dropped", @@ -102,6 +103,7 @@ static const char *bnad_net_stats_strings[BNAD_ETHTOOL_STATS_NUM] = { "tx_unmap_q_alloc_failed", "rx_unmap_q_alloc_failed", "rxbuf_alloc_failed", + "rxbuf_map_failed", "mac_stats_clr_cnt", "mac_frame_64", @@ -807,6 +809,7 @@ bnad_per_q_stats_fill(struct bnad *bnad, u64 *buf, int bi) rx_packets_with_error; buf[bi++] = rcb->rxq-> rxbuf_alloc_failed; + buf[bi++] = rcb->rxq->rxbuf_map_failed; buf[bi++] = rcb->producer_index; buf[bi++] = rcb->consumer_index; } @@ -821,6 +824,7 @@ bnad_per_q_stats_fill(struct bnad *bnad, u64 *buf, int bi) rx_packets_with_error; buf[bi++] = rcb->rxq-> rxbuf_alloc_failed; + buf[bi++] = rcb->rxq->rxbuf_map_failed; buf[bi++] = rcb->producer_index; buf[bi++] = rcb->consumer_index; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index fa0c7b54ec7a..634e50c8c5ef 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -47,6 +47,7 @@ #include <linux/timer.h> #include <linux/vmalloc.h> #include <linux/etherdevice.h> +#include <linux/net_tstamp.h> #include <asm/io.h> #include "cxgb4_uld.h" @@ -478,6 +479,8 @@ struct port_info { #ifdef CONFIG_CHELSIO_T4_FCOE struct cxgb_fcoe fcoe; #endif /* CONFIG_CHELSIO_T4_FCOE */ + bool rxtstamp; /* Enable TS */ + struct hwtstamp_config tstamp_config; }; struct dentry; @@ -517,6 +520,7 @@ struct sge_fl { /* SGE free-buffer queue state */ /* A packet gather list */ struct pkt_gl { + u64 sgetstamp; /* SGE Time Stamp for Ingress Packet */ struct page_frag frags[MAX_SKB_FRAGS]; void *va; /* virtual address of first byte */ unsigned int nfrags; /* # of fragments */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 0a87a3247464..4269944c5db5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -940,6 +940,7 @@ static const char * const devlog_level_strings[] = { static const char * const devlog_facility_strings[] = { [FW_DEVLOG_FACILITY_CORE] = "CORE", + [FW_DEVLOG_FACILITY_CF] = "CF", [FW_DEVLOG_FACILITY_SCHED] = "SCHED", [FW_DEVLOG_FACILITY_TIMER] = "TIMER", [FW_DEVLOG_FACILITY_RES] = "RES", @@ -1128,18 +1129,26 @@ static const struct file_operations devlog_fops = { static int mbox_show(struct seq_file *seq, void *v) { static const char * const owner[] = { "none", "FW", "driver", - "unknown" }; + "unknown", "<unread>" }; int i; unsigned int mbox = (uintptr_t)seq->private & 7; struct adapter *adap = seq->private - mbox; void __iomem *addr = adap->regs + PF_REG(mbox, CIM_PF_MAILBOX_DATA_A); - unsigned int ctrl_reg = (is_t4(adap->params.chip) - ? CIM_PF_MAILBOX_CTRL_A - : CIM_PF_MAILBOX_CTRL_SHADOW_COPY_A); - void __iomem *ctrl = adap->regs + PF_REG(mbox, ctrl_reg); - i = MBOWNER_G(readl(ctrl)); + /* For T4 we don't have a shadow copy of the Mailbox Control register. + * And since reading that real register causes a side effect of + * granting ownership, we're best of simply not reading it at all. + */ + if (is_t4(adap->params.chip)) { + i = 4; /* index of "<unread>" */ + } else { + unsigned int ctrl_reg = CIM_PF_MAILBOX_CTRL_SHADOW_COPY_A; + void __iomem *ctrl = adap->regs + PF_REG(mbox, ctrl_reg); + + i = MBOWNER_G(readl(ctrl)); + } + seq_printf(seq, "mailbox owned by %s\n\n", owner[i]); for (i = 0; i < MBOX_LEN; i += 8) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index 5eedb98ff581..02e4e028a647 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -961,6 +961,20 @@ static int set_flash(struct net_device *netdev, struct ethtool_flash *ef) return ret; } +static int get_ts_info(struct net_device *dev, struct ethtool_ts_info *ts_info) +{ + ts_info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + + ts_info->so_timestamping |= SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + ts_info->phc_index = -1; + + return 0; +} + static u32 get_rss_table_size(struct net_device *dev) { const struct port_info *pi = netdev_priv(dev); @@ -1095,6 +1109,7 @@ static const struct ethtool_ops cxgb_ethtool_ops = { .get_rxfh = get_rss_table, .set_rxfh = set_rss_table, .flash_device = set_flash, + .get_ts_info = get_ts_info }; void cxgb4_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index f5dcde27e402..c29227ee9ee8 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -275,7 +275,7 @@ static void link_report(struct net_device *dev) else { static const char *fc[] = { "no", "Rx", "Tx", "Tx/Rx" }; - const char *s = "10Mbps"; + const char *s; const struct port_info *p = netdev_priv(dev); switch (p->link_cfg.speed) { @@ -291,6 +291,10 @@ static void link_report(struct net_device *dev) case 40000: s = "40Gbps"; break; + default: + pr_info("%s: unsupported speed: %d\n", + dev->name, p->link_cfg.speed); + return; } netdev_info(dev, "link up, %s, full-duplex, %s PAUSE\n", s, @@ -2959,6 +2963,30 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) ret = t4_mdio_wr(pi->adapter, mbox, prtad, devad, data->reg_num, data->val_in); break; + case SIOCGHWTSTAMP: + return copy_to_user(req->ifr_data, &pi->tstamp_config, + sizeof(pi->tstamp_config)) ? + -EFAULT : 0; + case SIOCSHWTSTAMP: + if (copy_from_user(&pi->tstamp_config, req->ifr_data, + sizeof(pi->tstamp_config))) + return -EFAULT; + + switch (pi->tstamp_config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + pi->rxtstamp = false; + break; + case HWTSTAMP_FILTER_ALL: + pi->rxtstamp = true; + break; + default: + pi->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; + return -ERANGE; + } + + return copy_to_user(req->ifr_data, &pi->tstamp_config, + sizeof(pi->tstamp_config)) ? + -EFAULT : 0; default: return -EOPNOTSUPP; } @@ -3670,7 +3698,7 @@ static int adap_init0(struct adapter *adap) t4_get_tp_version(adap, &adap->params.tp_vers); ret = t4_check_fw_version(adap); /* If firmware is too old (not supported by driver) force an update. */ - if (ret == -EFAULT) + if (ret) state = DEV_STATE_UNINIT; if ((adap->flags & MASTER_PF) && state != DEV_STATE_INIT) { struct fw_info *fw_info; diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 9162746d7729..b7b93e7a643d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -1820,11 +1820,34 @@ static noinline int handle_trace_pkt(struct adapter *adap, return 0; } +/** + * cxgb4_sgetim_to_hwtstamp - convert sge time stamp to hw time stamp + * @adap: the adapter + * @hwtstamps: time stamp structure to update + * @sgetstamp: 60bit iqe timestamp + * + * Every ingress queue entry has the 60-bit timestamp, convert that timestamp + * which is in Core Clock ticks into ktime_t and assign it + **/ +static void cxgb4_sgetim_to_hwtstamp(struct adapter *adap, + struct skb_shared_hwtstamps *hwtstamps, + u64 sgetstamp) +{ + u64 ns; + u64 tmp = (sgetstamp * 1000 * 1000 + adap->params.vpd.cclk / 2); + + ns = div_u64(tmp, adap->params.vpd.cclk); + + memset(hwtstamps, 0, sizeof(*hwtstamps)); + hwtstamps->hwtstamp = ns_to_ktime(ns); +} + static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl, const struct cpl_rx_pkt *pkt) { struct adapter *adapter = rxq->rspq.adap; struct sge *s = &adapter->sge; + struct port_info *pi; int ret; struct sk_buff *skb; @@ -1842,6 +1865,10 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl, skb->ip_summed = CHECKSUM_UNNECESSARY; skb_record_rx_queue(skb, rxq->rspq.idx); skb_mark_napi_id(skb, &rxq->rspq.napi); + pi = netdev_priv(skb->dev); + if (pi->rxtstamp) + cxgb4_sgetim_to_hwtstamp(adapter, skb_hwtstamps(skb), + gl->sgetstamp); if (rxq->rspq.netdev->features & NETIF_F_RXHASH) skb_set_hash(skb, (__force u32)pkt->rsshdr.hash_val, PKT_HASH_TYPE_L3); @@ -1877,9 +1904,7 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, struct sge *s = &q->adap->sge; int cpl_trace_pkt = is_t4(q->adap->params.chip) ? CPL_TRACE_PKT : CPL_TRACE_PKT_T5; -#ifdef CONFIG_CHELSIO_T4_FCOE struct port_info *pi; -#endif if (unlikely(*(u8 *)rsp == cpl_trace_pkt)) return handle_trace_pkt(q->adap, si); @@ -1910,6 +1935,10 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, rxq->stats.pkts++; + pi = netdev_priv(skb->dev); + if (pi->rxtstamp) + cxgb4_sgetim_to_hwtstamp(q->adap, skb_hwtstamps(skb), + si->sgetstamp); if (csum_ok && (pkt->l2info & htonl(RXF_UDP_F | RXF_TCP_F))) { if (!pkt->ip_frag) { skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -1926,7 +1955,6 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, #define CPL_RX_PKT_FLAGS (RXF_PSH_F | RXF_SYN_F | RXF_UDP_F | \ RXF_TCP_F | RXF_IP_F | RXF_IP6_F | RXF_LRO_F) - pi = netdev_priv(skb->dev); if (!(pkt->l2info & cpu_to_be32(CPL_RX_PKT_FLAGS))) { if ((pkt->l2info & cpu_to_be32(RXF_FCOE_F)) && (pi->fcoe.flags & CXGB_FCOE_ENABLED)) { @@ -2067,6 +2095,8 @@ static int process_responses(struct sge_rspq *q, int budget) unmap_rx_buf(q->adap, &rxq->fl); } + si.sgetstamp = SGE_TIMESTAMP_G( + be64_to_cpu(rc->last_flit)); /* * Last buffer remains mapped so explicitly make it * coherent for CPU access. diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 44806253c178..cf61a5869c6e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -699,50 +699,107 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) { static const unsigned int t4_reg_ranges[] = { 0x1008, 0x1108, - 0x1180, 0x11b4, + 0x1180, 0x1184, + 0x1190, 0x1194, + 0x11a0, 0x11a4, + 0x11b0, 0x11b4, 0x11fc, 0x123c, 0x1300, 0x173c, 0x1800, 0x18fc, - 0x3000, 0x305c, - 0x3068, 0x30d8, - 0x30e0, 0x5924, - 0x5960, 0x59d4, - 0x5a00, 0x5af8, + 0x3000, 0x30d8, + 0x30e0, 0x30e4, + 0x30ec, 0x5910, + 0x5920, 0x5924, + 0x5960, 0x5960, + 0x5968, 0x5968, + 0x5970, 0x5970, + 0x5978, 0x5978, + 0x5980, 0x5980, + 0x5988, 0x5988, + 0x5990, 0x5990, + 0x5998, 0x5998, + 0x59a0, 0x59d4, + 0x5a00, 0x5ae0, + 0x5ae8, 0x5ae8, + 0x5af0, 0x5af0, + 0x5af8, 0x5af8, 0x6000, 0x6098, 0x6100, 0x6150, 0x6200, 0x6208, 0x6240, 0x6248, - 0x6280, 0x6338, + 0x6280, 0x62b0, + 0x62c0, 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, + 0x6a00, 0x6a04, + 0x6a14, 0x6a38, + 0x6a60, 0x6a70, + 0x6a78, 0x6a78, + 0x6b00, 0x6b0c, + 0x6b1c, 0x6b84, + 0x6bf0, 0x6bf8, + 0x6c00, 0x6c0c, + 0x6c1c, 0x6c84, + 0x6cf0, 0x6cf8, + 0x6d00, 0x6d0c, + 0x6d1c, 0x6d84, + 0x6df0, 0x6df8, + 0x6e00, 0x6e0c, + 0x6e1c, 0x6e84, + 0x6ef0, 0x6ef8, + 0x6f00, 0x6f0c, + 0x6f1c, 0x6f84, + 0x6ff0, 0x6ff8, + 0x7000, 0x700c, + 0x701c, 0x7084, + 0x70f0, 0x70f8, + 0x7100, 0x710c, + 0x711c, 0x7184, + 0x71f0, 0x71f8, + 0x7200, 0x720c, + 0x721c, 0x7284, + 0x72f0, 0x72f8, + 0x7300, 0x730c, + 0x731c, 0x7384, + 0x73f0, 0x73f8, + 0x7400, 0x7450, 0x7500, 0x7530, - 0x7600, 0x761c, + 0x7600, 0x760c, + 0x7614, 0x761c, 0x7680, 0x76cc, 0x7700, 0x7798, 0x77c0, 0x77fc, 0x7900, 0x79fc, - 0x7b00, 0x7c38, - 0x7d00, 0x7efc, - 0x8dc0, 0x8e1c, + 0x7b00, 0x7b58, + 0x7b60, 0x7b84, + 0x7b8c, 0x7c38, + 0x7d00, 0x7d38, + 0x7d40, 0x7d80, + 0x7d8c, 0x7ddc, + 0x7de4, 0x7e04, + 0x7e10, 0x7e1c, + 0x7e24, 0x7e38, + 0x7e40, 0x7e44, + 0x7e4c, 0x7e78, + 0x7e80, 0x7ea4, + 0x7eac, 0x7edc, + 0x7ee8, 0x7efc, + 0x8dc0, 0x8e04, + 0x8e10, 0x8e1c, 0x8e30, 0x8e78, - 0x8ea0, 0x8f6c, - 0x8fc0, 0x9074, + 0x8ea0, 0x8eb8, + 0x8ec0, 0x8f6c, + 0x8fc0, 0x9008, + 0x9010, 0x9058, + 0x9060, 0x9060, + 0x9068, 0x9074, 0x90fc, 0x90fc, - 0x9400, 0x9458, - 0x9600, 0x96bc, + 0x9400, 0x9408, + 0x9410, 0x9458, + 0x9600, 0x9600, + 0x9608, 0x9638, + 0x9640, 0x96bc, 0x9800, 0x9808, 0x9820, 0x983c, 0x9850, 0x9864, @@ -754,23 +811,42 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x9e80, 0x9eec, 0x9f00, 0x9f6c, 0x9f80, 0x9fec, - 0xd004, 0xd03c, + 0xd004, 0xd004, + 0xd010, 0xd03c, 0xdfc0, 0xdfe0, 0xe000, 0xea7c, - 0xf000, 0x11110, - 0x11118, 0x11190, + 0xf000, 0x11190, 0x19040, 0x1906c, 0x19078, 0x19080, - 0x1908c, 0x19124, - 0x19150, 0x191b0, + 0x1908c, 0x190e4, + 0x190f0, 0x190f8, + 0x19100, 0x19110, + 0x19120, 0x19124, + 0x19150, 0x19194, + 0x1919c, 0x191b0, 0x191d0, 0x191e8, 0x19238, 0x1924c, - 0x193f8, 0x19474, - 0x19490, 0x194f8, - 0x19800, 0x19f4c, - 0x1a000, 0x1a06c, - 0x1a0b0, 0x1a120, - 0x1a128, 0x1a138, + 0x193f8, 0x1943c, + 0x1944c, 0x19474, + 0x19490, 0x194e0, + 0x194f0, 0x194f8, + 0x19800, 0x19c08, + 0x19c10, 0x19c90, + 0x19ca0, 0x19ce4, + 0x19cf0, 0x19d40, + 0x19d50, 0x19d94, + 0x19da0, 0x19de8, + 0x19df0, 0x19e40, + 0x19e50, 0x19e90, + 0x19ea0, 0x19f4c, + 0x1a000, 0x1a004, + 0x1a010, 0x1a06c, + 0x1a0b0, 0x1a0e4, + 0x1a0ec, 0x1a0f4, + 0x1a100, 0x1a108, + 0x1a114, 0x1a120, + 0x1a128, 0x1a130, + 0x1a138, 0x1a138, 0x1a190, 0x1a1c4, 0x1a1fc, 0x1a1fc, 0x1e040, 0x1e04c, @@ -823,9 +899,12 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x1ffc0, 0x1ffc8, 0x20000, 0x2002c, 0x20100, 0x2013c, - 0x20190, 0x201c8, + 0x20190, 0x201a0, + 0x201a8, 0x201b8, + 0x201c4, 0x201c8, 0x20200, 0x20318, - 0x20400, 0x20528, + 0x20400, 0x204b4, + 0x204c0, 0x20528, 0x20540, 0x20614, 0x21000, 0x21040, 0x2104c, 0x21060, @@ -834,22 +913,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x21270, 0x21284, 0x212fc, 0x21388, 0x21400, 0x21404, - 0x21500, 0x21518, - 0x2152c, 0x2153c, + 0x21500, 0x21500, + 0x21510, 0x21518, + 0x2152c, 0x21530, + 0x2153c, 0x2153c, 0x21550, 0x21554, 0x21600, 0x21600, - 0x21608, 0x21628, - 0x21630, 0x2163c, + 0x21608, 0x2161c, + 0x21624, 0x21628, + 0x21630, 0x21634, + 0x2163c, 0x2163c, 0x21700, 0x2171c, 0x21780, 0x2178c, - 0x21800, 0x21c38, - 0x21c80, 0x21d7c, + 0x21800, 0x21818, + 0x21820, 0x21828, + 0x21830, 0x21848, + 0x21850, 0x21854, + 0x21860, 0x21868, + 0x21870, 0x21870, + 0x21878, 0x21898, + 0x218a0, 0x218a8, + 0x218b0, 0x218c8, + 0x218d0, 0x218d4, + 0x218e0, 0x218e8, + 0x218f0, 0x218f0, + 0x218f8, 0x21a18, + 0x21a20, 0x21a28, + 0x21a30, 0x21a48, + 0x21a50, 0x21a54, + 0x21a60, 0x21a68, + 0x21a70, 0x21a70, + 0x21a78, 0x21a98, + 0x21aa0, 0x21aa8, + 0x21ab0, 0x21ac8, + 0x21ad0, 0x21ad4, + 0x21ae0, 0x21ae8, + 0x21af0, 0x21af0, + 0x21af8, 0x21c18, + 0x21c20, 0x21c20, + 0x21c28, 0x21c30, + 0x21c38, 0x21c38, + 0x21c80, 0x21c98, + 0x21ca0, 0x21ca8, + 0x21cb0, 0x21cc8, + 0x21cd0, 0x21cd4, + 0x21ce0, 0x21ce8, + 0x21cf0, 0x21cf0, + 0x21cf8, 0x21d7c, 0x21e00, 0x21e04, 0x22000, 0x2202c, 0x22100, 0x2213c, - 0x22190, 0x221c8, + 0x22190, 0x221a0, + 0x221a8, 0x221b8, + 0x221c4, 0x221c8, 0x22200, 0x22318, - 0x22400, 0x22528, + 0x22400, 0x224b4, + 0x224c0, 0x22528, 0x22540, 0x22614, 0x23000, 0x23040, 0x2304c, 0x23060, @@ -858,22 +977,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x23270, 0x23284, 0x232fc, 0x23388, 0x23400, 0x23404, - 0x23500, 0x23518, - 0x2352c, 0x2353c, + 0x23500, 0x23500, + 0x23510, 0x23518, + 0x2352c, 0x23530, + 0x2353c, 0x2353c, 0x23550, 0x23554, 0x23600, 0x23600, - 0x23608, 0x23628, - 0x23630, 0x2363c, + 0x23608, 0x2361c, + 0x23624, 0x23628, + 0x23630, 0x23634, + 0x2363c, 0x2363c, 0x23700, 0x2371c, 0x23780, 0x2378c, - 0x23800, 0x23c38, - 0x23c80, 0x23d7c, + 0x23800, 0x23818, + 0x23820, 0x23828, + 0x23830, 0x23848, + 0x23850, 0x23854, + 0x23860, 0x23868, + 0x23870, 0x23870, + 0x23878, 0x23898, + 0x238a0, 0x238a8, + 0x238b0, 0x238c8, + 0x238d0, 0x238d4, + 0x238e0, 0x238e8, + 0x238f0, 0x238f0, + 0x238f8, 0x23a18, + 0x23a20, 0x23a28, + 0x23a30, 0x23a48, + 0x23a50, 0x23a54, + 0x23a60, 0x23a68, + 0x23a70, 0x23a70, + 0x23a78, 0x23a98, + 0x23aa0, 0x23aa8, + 0x23ab0, 0x23ac8, + 0x23ad0, 0x23ad4, + 0x23ae0, 0x23ae8, + 0x23af0, 0x23af0, + 0x23af8, 0x23c18, + 0x23c20, 0x23c20, + 0x23c28, 0x23c30, + 0x23c38, 0x23c38, + 0x23c80, 0x23c98, + 0x23ca0, 0x23ca8, + 0x23cb0, 0x23cc8, + 0x23cd0, 0x23cd4, + 0x23ce0, 0x23ce8, + 0x23cf0, 0x23cf0, + 0x23cf8, 0x23d7c, 0x23e00, 0x23e04, 0x24000, 0x2402c, 0x24100, 0x2413c, - 0x24190, 0x241c8, + 0x24190, 0x241a0, + 0x241a8, 0x241b8, + 0x241c4, 0x241c8, 0x24200, 0x24318, - 0x24400, 0x24528, + 0x24400, 0x244b4, + 0x244c0, 0x24528, 0x24540, 0x24614, 0x25000, 0x25040, 0x2504c, 0x25060, @@ -882,22 +1041,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x25270, 0x25284, 0x252fc, 0x25388, 0x25400, 0x25404, - 0x25500, 0x25518, - 0x2552c, 0x2553c, + 0x25500, 0x25500, + 0x25510, 0x25518, + 0x2552c, 0x25530, + 0x2553c, 0x2553c, 0x25550, 0x25554, 0x25600, 0x25600, - 0x25608, 0x25628, - 0x25630, 0x2563c, + 0x25608, 0x2561c, + 0x25624, 0x25628, + 0x25630, 0x25634, + 0x2563c, 0x2563c, 0x25700, 0x2571c, 0x25780, 0x2578c, - 0x25800, 0x25c38, - 0x25c80, 0x25d7c, + 0x25800, 0x25818, + 0x25820, 0x25828, + 0x25830, 0x25848, + 0x25850, 0x25854, + 0x25860, 0x25868, + 0x25870, 0x25870, + 0x25878, 0x25898, + 0x258a0, 0x258a8, + 0x258b0, 0x258c8, + 0x258d0, 0x258d4, + 0x258e0, 0x258e8, + 0x258f0, 0x258f0, + 0x258f8, 0x25a18, + 0x25a20, 0x25a28, + 0x25a30, 0x25a48, + 0x25a50, 0x25a54, + 0x25a60, 0x25a68, + 0x25a70, 0x25a70, + 0x25a78, 0x25a98, + 0x25aa0, 0x25aa8, + 0x25ab0, 0x25ac8, + 0x25ad0, 0x25ad4, + 0x25ae0, 0x25ae8, + 0x25af0, 0x25af0, + 0x25af8, 0x25c18, + 0x25c20, 0x25c20, + 0x25c28, 0x25c30, + 0x25c38, 0x25c38, + 0x25c80, 0x25c98, + 0x25ca0, 0x25ca8, + 0x25cb0, 0x25cc8, + 0x25cd0, 0x25cd4, + 0x25ce0, 0x25ce8, + 0x25cf0, 0x25cf0, + 0x25cf8, 0x25d7c, 0x25e00, 0x25e04, 0x26000, 0x2602c, 0x26100, 0x2613c, - 0x26190, 0x261c8, + 0x26190, 0x261a0, + 0x261a8, 0x261b8, + 0x261c4, 0x261c8, 0x26200, 0x26318, - 0x26400, 0x26528, + 0x26400, 0x264b4, + 0x264c0, 0x26528, 0x26540, 0x26614, 0x27000, 0x27040, 0x2704c, 0x27060, @@ -906,51 +1105,120 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x27270, 0x27284, 0x272fc, 0x27388, 0x27400, 0x27404, - 0x27500, 0x27518, - 0x2752c, 0x2753c, + 0x27500, 0x27500, + 0x27510, 0x27518, + 0x2752c, 0x27530, + 0x2753c, 0x2753c, 0x27550, 0x27554, 0x27600, 0x27600, - 0x27608, 0x27628, - 0x27630, 0x2763c, + 0x27608, 0x2761c, + 0x27624, 0x27628, + 0x27630, 0x27634, + 0x2763c, 0x2763c, 0x27700, 0x2771c, 0x27780, 0x2778c, - 0x27800, 0x27c38, - 0x27c80, 0x27d7c, + 0x27800, 0x27818, + 0x27820, 0x27828, + 0x27830, 0x27848, + 0x27850, 0x27854, + 0x27860, 0x27868, + 0x27870, 0x27870, + 0x27878, 0x27898, + 0x278a0, 0x278a8, + 0x278b0, 0x278c8, + 0x278d0, 0x278d4, + 0x278e0, 0x278e8, + 0x278f0, 0x278f0, + 0x278f8, 0x27a18, + 0x27a20, 0x27a28, + 0x27a30, 0x27a48, + 0x27a50, 0x27a54, + 0x27a60, 0x27a68, + 0x27a70, 0x27a70, + 0x27a78, 0x27a98, + 0x27aa0, 0x27aa8, + 0x27ab0, 0x27ac8, + 0x27ad0, 0x27ad4, + 0x27ae0, 0x27ae8, + 0x27af0, 0x27af0, + 0x27af8, 0x27c18, + 0x27c20, 0x27c20, + 0x27c28, 0x27c30, + 0x27c38, 0x27c38, + 0x27c80, 0x27c98, + 0x27ca0, 0x27ca8, + 0x27cb0, 0x27cc8, + 0x27cd0, 0x27cd4, + 0x27ce0, 0x27ce8, + 0x27cf0, 0x27cf0, + 0x27cf8, 0x27d7c, 0x27e00, 0x27e04, }; static const unsigned int t5_reg_ranges[] = { - 0x1008, 0x1148, - 0x1180, 0x11b4, + 0x1008, 0x10c0, + 0x10cc, 0x10f8, + 0x1100, 0x1100, + 0x110c, 0x1148, + 0x1180, 0x1184, + 0x1190, 0x1194, + 0x11a0, 0x11a4, + 0x11b0, 0x11b4, 0x11fc, 0x123c, 0x1280, 0x173c, 0x1800, 0x18fc, 0x3000, 0x3028, - 0x3068, 0x30d8, + 0x3060, 0x30b0, + 0x30b8, 0x30d8, 0x30e0, 0x30fc, 0x3140, 0x357c, 0x35a8, 0x35cc, 0x35ec, 0x35ec, 0x3600, 0x5624, - 0x56cc, 0x575c, + 0x56cc, 0x56ec, + 0x56f4, 0x5720, + 0x5728, 0x575c, 0x580c, 0x5814, - 0x5890, 0x58bc, - 0x5940, 0x59dc, + 0x5890, 0x589c, + 0x58a4, 0x58ac, + 0x58b8, 0x58bc, + 0x5940, 0x59c8, + 0x59d0, 0x59dc, 0x59fc, 0x5a18, - 0x5a60, 0x5a9c, + 0x5a60, 0x5a70, + 0x5a80, 0x5a9c, 0x5b94, 0x5bfc, - 0x6000, 0x6040, - 0x6058, 0x614c, + 0x6000, 0x6020, + 0x6028, 0x6040, + 0x6058, 0x609c, + 0x60a8, 0x614c, 0x7700, 0x7798, 0x77c0, 0x78fc, - 0x7b00, 0x7c54, - 0x7d00, 0x7efc, + 0x7b00, 0x7b58, + 0x7b60, 0x7b84, + 0x7b8c, 0x7c54, + 0x7d00, 0x7d38, + 0x7d40, 0x7d80, + 0x7d8c, 0x7ddc, + 0x7de4, 0x7e04, + 0x7e10, 0x7e1c, + 0x7e24, 0x7e38, + 0x7e40, 0x7e44, + 0x7e4c, 0x7e78, + 0x7e80, 0x7edc, + 0x7ee8, 0x7efc, 0x8dc0, 0x8de0, - 0x8df8, 0x8e84, + 0x8df8, 0x8e04, + 0x8e10, 0x8e84, 0x8ea0, 0x8f84, - 0x8fc0, 0x90f8, - 0x9400, 0x9470, - 0x9600, 0x96f4, + 0x8fc0, 0x9058, + 0x9060, 0x9060, + 0x9068, 0x90f8, + 0x9400, 0x9408, + 0x9410, 0x9470, + 0x9600, 0x9600, + 0x9608, 0x9638, + 0x9640, 0x96f4, 0x9800, 0x9808, 0x9820, 0x983c, 0x9850, 0x9864, @@ -962,103 +1230,143 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x9e80, 0x9eec, 0x9f00, 0x9f6c, 0x9f80, 0xa020, - 0xd004, 0xd03c, + 0xd004, 0xd004, + 0xd010, 0xd03c, 0xdfc0, 0xdfe0, - 0xe000, 0x11088, - 0x1109c, 0x11110, - 0x11118, 0x1117c, + 0xe000, 0x1106c, + 0x11074, 0x11088, + 0x1109c, 0x1117c, 0x11190, 0x11204, 0x19040, 0x1906c, 0x19078, 0x19080, - 0x1908c, 0x19124, - 0x19150, 0x191b0, + 0x1908c, 0x190e8, + 0x190f0, 0x190f8, + 0x19100, 0x19110, + 0x19120, 0x19124, + 0x19150, 0x19194, + 0x1919c, 0x191b0, 0x191d0, 0x191e8, 0x19238, 0x19290, - 0x193f8, 0x19474, + 0x193f8, 0x19428, + 0x19430, 0x19444, + 0x1944c, 0x1946c, + 0x19474, 0x19474, 0x19490, 0x194cc, 0x194f0, 0x194f8, - 0x19c00, 0x19c60, - 0x19c94, 0x19e10, - 0x19e50, 0x19f34, + 0x19c00, 0x19c08, + 0x19c10, 0x19c60, + 0x19c94, 0x19ce4, + 0x19cf0, 0x19d40, + 0x19d50, 0x19d94, + 0x19da0, 0x19de8, + 0x19df0, 0x19e10, + 0x19e50, 0x19e90, + 0x19ea0, 0x19f24, + 0x19f34, 0x19f34, 0x19f40, 0x19f50, - 0x19f90, 0x19fe4, - 0x1a000, 0x1a06c, - 0x1a0b0, 0x1a120, - 0x1a128, 0x1a138, + 0x19f90, 0x19fb4, + 0x19fc4, 0x19fe4, + 0x1a000, 0x1a004, + 0x1a010, 0x1a06c, + 0x1a0b0, 0x1a0e4, + 0x1a0ec, 0x1a0f8, + 0x1a100, 0x1a108, + 0x1a114, 0x1a120, + 0x1a128, 0x1a130, + 0x1a138, 0x1a138, 0x1a190, 0x1a1c4, 0x1a1fc, 0x1a1fc, 0x1e008, 0x1e00c, - 0x1e040, 0x1e04c, + 0x1e040, 0x1e044, + 0x1e04c, 0x1e04c, 0x1e284, 0x1e290, 0x1e2c0, 0x1e2c0, 0x1e2e0, 0x1e2e0, 0x1e300, 0x1e384, 0x1e3c0, 0x1e3c8, 0x1e408, 0x1e40c, - 0x1e440, 0x1e44c, + 0x1e440, 0x1e444, + 0x1e44c, 0x1e44c, 0x1e684, 0x1e690, 0x1e6c0, 0x1e6c0, 0x1e6e0, 0x1e6e0, 0x1e700, 0x1e784, 0x1e7c0, 0x1e7c8, 0x1e808, 0x1e80c, - 0x1e840, 0x1e84c, + 0x1e840, 0x1e844, + 0x1e84c, 0x1e84c, 0x1ea84, 0x1ea90, 0x1eac0, 0x1eac0, 0x1eae0, 0x1eae0, 0x1eb00, 0x1eb84, 0x1ebc0, 0x1ebc8, 0x1ec08, 0x1ec0c, - 0x1ec40, 0x1ec4c, + 0x1ec40, 0x1ec44, + 0x1ec4c, 0x1ec4c, 0x1ee84, 0x1ee90, 0x1eec0, 0x1eec0, 0x1eee0, 0x1eee0, 0x1ef00, 0x1ef84, 0x1efc0, 0x1efc8, 0x1f008, 0x1f00c, - 0x1f040, 0x1f04c, + 0x1f040, 0x1f044, + 0x1f04c, 0x1f04c, 0x1f284, 0x1f290, 0x1f2c0, 0x1f2c0, 0x1f2e0, 0x1f2e0, 0x1f300, 0x1f384, 0x1f3c0, 0x1f3c8, 0x1f408, 0x1f40c, - 0x1f440, 0x1f44c, + 0x1f440, 0x1f444, + 0x1f44c, 0x1f44c, 0x1f684, 0x1f690, 0x1f6c0, 0x1f6c0, 0x1f6e0, 0x1f6e0, 0x1f700, 0x1f784, 0x1f7c0, 0x1f7c8, 0x1f808, 0x1f80c, - 0x1f840, 0x1f84c, + 0x1f840, 0x1f844, + 0x1f84c, 0x1f84c, 0x1fa84, 0x1fa90, 0x1fac0, 0x1fac0, 0x1fae0, 0x1fae0, 0x1fb00, 0x1fb84, 0x1fbc0, 0x1fbc8, 0x1fc08, 0x1fc0c, - 0x1fc40, 0x1fc4c, + 0x1fc40, 0x1fc44, + 0x1fc4c, 0x1fc4c, 0x1fe84, 0x1fe90, 0x1fec0, 0x1fec0, 0x1fee0, 0x1fee0, 0x1ff00, 0x1ff84, 0x1ffc0, 0x1ffc8, 0x30000, 0x30030, + 0x30038, 0x30038, + 0x30040, 0x30040, 0x30100, 0x30144, - 0x30190, 0x301d0, + 0x30190, 0x301a0, + 0x301a8, 0x301b8, + 0x301c4, 0x301c8, + 0x301d0, 0x301d0, 0x30200, 0x30318, - 0x30400, 0x3052c, + 0x30400, 0x304b4, + 0x304c0, 0x3052c, 0x30540, 0x3061c, - 0x30800, 0x30834, + 0x30800, 0x30828, + 0x30834, 0x30834, 0x308c0, 0x30908, 0x30910, 0x309ac, - 0x30a00, 0x30a2c, + 0x30a00, 0x30a14, + 0x30a1c, 0x30a2c, 0x30a44, 0x30a50, - 0x30a74, 0x30c24, + 0x30a74, 0x30a74, + 0x30a7c, 0x30afc, + 0x30b08, 0x30c24, 0x30d00, 0x30d00, 0x30d08, 0x30d14, 0x30d1c, 0x30d20, - 0x30d3c, 0x30d50, + 0x30d3c, 0x30d3c, + 0x30d48, 0x30d50, 0x31200, 0x3120c, 0x31220, 0x31220, 0x31240, 0x31240, @@ -1078,27 +1386,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x322c8, 0x322fc, 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, + 0x32b00, 0x32b10, + 0x32b20, 0x32b30, + 0x32b40, 0x32b50, + 0x32b60, 0x32b70, + 0x33000, 0x33028, + 0x33030, 0x33048, + 0x33060, 0x33068, + 0x33070, 0x3309c, + 0x330f0, 0x33128, + 0x33130, 0x33148, + 0x33160, 0x33168, + 0x33170, 0x3319c, + 0x331f0, 0x33238, + 0x33240, 0x33240, + 0x33248, 0x33250, + 0x3325c, 0x33264, + 0x33270, 0x332b8, + 0x332c0, 0x332e4, + 0x332f8, 0x33338, + 0x33340, 0x33340, + 0x33348, 0x33350, + 0x3335c, 0x33364, + 0x33370, 0x333b8, + 0x333c0, 0x333e4, + 0x333f8, 0x33428, + 0x33430, 0x33448, + 0x33460, 0x33468, + 0x33470, 0x3349c, + 0x334f0, 0x33528, + 0x33530, 0x33548, + 0x33560, 0x33568, + 0x33570, 0x3359c, + 0x335f0, 0x33638, + 0x33640, 0x33640, + 0x33648, 0x33650, + 0x3365c, 0x33664, + 0x33670, 0x336b8, + 0x336c0, 0x336e4, + 0x336f8, 0x33738, + 0x33740, 0x33740, + 0x33748, 0x33750, + 0x3375c, 0x33764, + 0x33770, 0x337b8, + 0x337c0, 0x337e4, 0x337f8, 0x337fc, 0x33814, 0x33814, 0x3382c, 0x3382c, 0x33880, 0x3388c, 0x338e8, 0x338ec, - 0x33900, 0x33948, - 0x33960, 0x3399c, - 0x339f0, 0x33ae4, + 0x33900, 0x33928, + 0x33930, 0x33948, + 0x33960, 0x33968, + 0x33970, 0x3399c, + 0x339f0, 0x33a38, + 0x33a40, 0x33a40, + 0x33a48, 0x33a50, + 0x33a5c, 0x33a64, + 0x33a70, 0x33ab8, + 0x33ac0, 0x33ae4, 0x33af8, 0x33b10, 0x33b28, 0x33b28, 0x33b3c, 0x33b50, @@ -1107,21 +1453,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x33c3c, 0x33c50, 0x33cf0, 0x33cfc, 0x34000, 0x34030, + 0x34038, 0x34038, + 0x34040, 0x34040, 0x34100, 0x34144, - 0x34190, 0x341d0, + 0x34190, 0x341a0, + 0x341a8, 0x341b8, + 0x341c4, 0x341c8, + 0x341d0, 0x341d0, 0x34200, 0x34318, - 0x34400, 0x3452c, + 0x34400, 0x344b4, + 0x344c0, 0x3452c, 0x34540, 0x3461c, - 0x34800, 0x34834, + 0x34800, 0x34828, + 0x34834, 0x34834, 0x348c0, 0x34908, 0x34910, 0x349ac, - 0x34a00, 0x34a2c, + 0x34a00, 0x34a14, + 0x34a1c, 0x34a2c, 0x34a44, 0x34a50, - 0x34a74, 0x34c24, + 0x34a74, 0x34a74, + 0x34a7c, 0x34afc, + 0x34b08, 0x34c24, 0x34d00, 0x34d00, 0x34d08, 0x34d14, 0x34d1c, 0x34d20, - 0x34d3c, 0x34d50, + 0x34d3c, 0x34d3c, + 0x34d48, 0x34d50, 0x35200, 0x3520c, 0x35220, 0x35220, 0x35240, 0x35240, @@ -1141,27 +1498,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x362c8, 0x362fc, 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, + 0x36b00, 0x36b10, + 0x36b20, 0x36b30, + 0x36b40, 0x36b50, + 0x36b60, 0x36b70, + 0x37000, 0x37028, + 0x37030, 0x37048, + 0x37060, 0x37068, + 0x37070, 0x3709c, + 0x370f0, 0x37128, + 0x37130, 0x37148, + 0x37160, 0x37168, + 0x37170, 0x3719c, + 0x371f0, 0x37238, + 0x37240, 0x37240, + 0x37248, 0x37250, + 0x3725c, 0x37264, + 0x37270, 0x372b8, + 0x372c0, 0x372e4, + 0x372f8, 0x37338, + 0x37340, 0x37340, + 0x37348, 0x37350, + 0x3735c, 0x37364, + 0x37370, 0x373b8, + 0x373c0, 0x373e4, + 0x373f8, 0x37428, + 0x37430, 0x37448, + 0x37460, 0x37468, + 0x37470, 0x3749c, + 0x374f0, 0x37528, + 0x37530, 0x37548, + 0x37560, 0x37568, + 0x37570, 0x3759c, + 0x375f0, 0x37638, + 0x37640, 0x37640, + 0x37648, 0x37650, + 0x3765c, 0x37664, + 0x37670, 0x376b8, + 0x376c0, 0x376e4, + 0x376f8, 0x37738, + 0x37740, 0x37740, + 0x37748, 0x37750, + 0x3775c, 0x37764, + 0x37770, 0x377b8, + 0x377c0, 0x377e4, 0x377f8, 0x377fc, 0x37814, 0x37814, 0x3782c, 0x3782c, 0x37880, 0x3788c, 0x378e8, 0x378ec, - 0x37900, 0x37948, - 0x37960, 0x3799c, - 0x379f0, 0x37ae4, + 0x37900, 0x37928, + 0x37930, 0x37948, + 0x37960, 0x37968, + 0x37970, 0x3799c, + 0x379f0, 0x37a38, + 0x37a40, 0x37a40, + 0x37a48, 0x37a50, + 0x37a5c, 0x37a64, + 0x37a70, 0x37ab8, + 0x37ac0, 0x37ae4, 0x37af8, 0x37b10, 0x37b28, 0x37b28, 0x37b3c, 0x37b50, @@ -1170,21 +1565,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x37c3c, 0x37c50, 0x37cf0, 0x37cfc, 0x38000, 0x38030, + 0x38038, 0x38038, + 0x38040, 0x38040, 0x38100, 0x38144, - 0x38190, 0x381d0, + 0x38190, 0x381a0, + 0x381a8, 0x381b8, + 0x381c4, 0x381c8, + 0x381d0, 0x381d0, 0x38200, 0x38318, - 0x38400, 0x3852c, + 0x38400, 0x384b4, + 0x384c0, 0x3852c, 0x38540, 0x3861c, - 0x38800, 0x38834, + 0x38800, 0x38828, + 0x38834, 0x38834, 0x388c0, 0x38908, 0x38910, 0x389ac, - 0x38a00, 0x38a2c, + 0x38a00, 0x38a14, + 0x38a1c, 0x38a2c, 0x38a44, 0x38a50, - 0x38a74, 0x38c24, + 0x38a74, 0x38a74, + 0x38a7c, 0x38afc, + 0x38b08, 0x38c24, 0x38d00, 0x38d00, 0x38d08, 0x38d14, 0x38d1c, 0x38d20, - 0x38d3c, 0x38d50, + 0x38d3c, 0x38d3c, + 0x38d48, 0x38d50, 0x39200, 0x3920c, 0x39220, 0x39220, 0x39240, 0x39240, @@ -1204,27 +1610,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3a2c8, 0x3a2fc, 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, + 0x3ab00, 0x3ab10, + 0x3ab20, 0x3ab30, + 0x3ab40, 0x3ab50, + 0x3ab60, 0x3ab70, + 0x3b000, 0x3b028, + 0x3b030, 0x3b048, + 0x3b060, 0x3b068, + 0x3b070, 0x3b09c, + 0x3b0f0, 0x3b128, + 0x3b130, 0x3b148, + 0x3b160, 0x3b168, + 0x3b170, 0x3b19c, + 0x3b1f0, 0x3b238, + 0x3b240, 0x3b240, + 0x3b248, 0x3b250, + 0x3b25c, 0x3b264, + 0x3b270, 0x3b2b8, + 0x3b2c0, 0x3b2e4, + 0x3b2f8, 0x3b338, + 0x3b340, 0x3b340, + 0x3b348, 0x3b350, + 0x3b35c, 0x3b364, + 0x3b370, 0x3b3b8, + 0x3b3c0, 0x3b3e4, + 0x3b3f8, 0x3b428, + 0x3b430, 0x3b448, + 0x3b460, 0x3b468, + 0x3b470, 0x3b49c, + 0x3b4f0, 0x3b528, + 0x3b530, 0x3b548, + 0x3b560, 0x3b568, + 0x3b570, 0x3b59c, + 0x3b5f0, 0x3b638, + 0x3b640, 0x3b640, + 0x3b648, 0x3b650, + 0x3b65c, 0x3b664, + 0x3b670, 0x3b6b8, + 0x3b6c0, 0x3b6e4, + 0x3b6f8, 0x3b738, + 0x3b740, 0x3b740, + 0x3b748, 0x3b750, + 0x3b75c, 0x3b764, + 0x3b770, 0x3b7b8, + 0x3b7c0, 0x3b7e4, 0x3b7f8, 0x3b7fc, 0x3b814, 0x3b814, 0x3b82c, 0x3b82c, 0x3b880, 0x3b88c, 0x3b8e8, 0x3b8ec, - 0x3b900, 0x3b948, - 0x3b960, 0x3b99c, - 0x3b9f0, 0x3bae4, + 0x3b900, 0x3b928, + 0x3b930, 0x3b948, + 0x3b960, 0x3b968, + 0x3b970, 0x3b99c, + 0x3b9f0, 0x3ba38, + 0x3ba40, 0x3ba40, + 0x3ba48, 0x3ba50, + 0x3ba5c, 0x3ba64, + 0x3ba70, 0x3bab8, + 0x3bac0, 0x3bae4, 0x3baf8, 0x3bb10, 0x3bb28, 0x3bb28, 0x3bb3c, 0x3bb50, @@ -1233,21 +1677,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3bc3c, 0x3bc50, 0x3bcf0, 0x3bcfc, 0x3c000, 0x3c030, + 0x3c038, 0x3c038, + 0x3c040, 0x3c040, 0x3c100, 0x3c144, - 0x3c190, 0x3c1d0, + 0x3c190, 0x3c1a0, + 0x3c1a8, 0x3c1b8, + 0x3c1c4, 0x3c1c8, + 0x3c1d0, 0x3c1d0, 0x3c200, 0x3c318, - 0x3c400, 0x3c52c, + 0x3c400, 0x3c4b4, + 0x3c4c0, 0x3c52c, 0x3c540, 0x3c61c, - 0x3c800, 0x3c834, + 0x3c800, 0x3c828, + 0x3c834, 0x3c834, 0x3c8c0, 0x3c908, 0x3c910, 0x3c9ac, - 0x3ca00, 0x3ca2c, + 0x3ca00, 0x3ca14, + 0x3ca1c, 0x3ca2c, 0x3ca44, 0x3ca50, - 0x3ca74, 0x3cc24, + 0x3ca74, 0x3ca74, + 0x3ca7c, 0x3cafc, + 0x3cb08, 0x3cc24, 0x3cd00, 0x3cd00, 0x3cd08, 0x3cd14, 0x3cd1c, 0x3cd20, - 0x3cd3c, 0x3cd50, + 0x3cd3c, 0x3cd3c, + 0x3cd48, 0x3cd50, 0x3d200, 0x3d20c, 0x3d220, 0x3d220, 0x3d240, 0x3d240, @@ -1267,27 +1722,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3e2c8, 0x3e2fc, 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, + 0x3eb00, 0x3eb10, + 0x3eb20, 0x3eb30, + 0x3eb40, 0x3eb50, + 0x3eb60, 0x3eb70, + 0x3f000, 0x3f028, + 0x3f030, 0x3f048, + 0x3f060, 0x3f068, + 0x3f070, 0x3f09c, + 0x3f0f0, 0x3f128, + 0x3f130, 0x3f148, + 0x3f160, 0x3f168, + 0x3f170, 0x3f19c, + 0x3f1f0, 0x3f238, + 0x3f240, 0x3f240, + 0x3f248, 0x3f250, + 0x3f25c, 0x3f264, + 0x3f270, 0x3f2b8, + 0x3f2c0, 0x3f2e4, + 0x3f2f8, 0x3f338, + 0x3f340, 0x3f340, + 0x3f348, 0x3f350, + 0x3f35c, 0x3f364, + 0x3f370, 0x3f3b8, + 0x3f3c0, 0x3f3e4, + 0x3f3f8, 0x3f428, + 0x3f430, 0x3f448, + 0x3f460, 0x3f468, + 0x3f470, 0x3f49c, + 0x3f4f0, 0x3f528, + 0x3f530, 0x3f548, + 0x3f560, 0x3f568, + 0x3f570, 0x3f59c, + 0x3f5f0, 0x3f638, + 0x3f640, 0x3f640, + 0x3f648, 0x3f650, + 0x3f65c, 0x3f664, + 0x3f670, 0x3f6b8, + 0x3f6c0, 0x3f6e4, + 0x3f6f8, 0x3f738, + 0x3f740, 0x3f740, + 0x3f748, 0x3f750, + 0x3f75c, 0x3f764, + 0x3f770, 0x3f7b8, + 0x3f7c0, 0x3f7e4, 0x3f7f8, 0x3f7fc, 0x3f814, 0x3f814, 0x3f82c, 0x3f82c, 0x3f880, 0x3f88c, 0x3f8e8, 0x3f8ec, - 0x3f900, 0x3f948, - 0x3f960, 0x3f99c, - 0x3f9f0, 0x3fae4, + 0x3f900, 0x3f928, + 0x3f930, 0x3f948, + 0x3f960, 0x3f968, + 0x3f970, 0x3f99c, + 0x3f9f0, 0x3fa38, + 0x3fa40, 0x3fa40, + 0x3fa48, 0x3fa50, + 0x3fa5c, 0x3fa64, + 0x3fa70, 0x3fab8, + 0x3fac0, 0x3fae4, 0x3faf8, 0x3fb10, 0x3fb28, 0x3fb28, 0x3fb3c, 0x3fb50, @@ -1296,108 +1789,224 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3fc3c, 0x3fc50, 0x3fcf0, 0x3fcfc, 0x40000, 0x4000c, - 0x40040, 0x40068, - 0x4007c, 0x40144, + 0x40040, 0x40050, + 0x40060, 0x40068, + 0x4007c, 0x4008c, + 0x40094, 0x400b0, + 0x400c0, 0x40144, 0x40180, 0x4018c, - 0x40200, 0x40298, - 0x402ac, 0x4033c, + 0x40200, 0x40254, + 0x40260, 0x40264, + 0x40270, 0x40288, + 0x40290, 0x40298, + 0x402ac, 0x402c8, + 0x402d0, 0x402e0, + 0x402f0, 0x402f0, + 0x40300, 0x4033c, 0x403f8, 0x403fc, 0x41304, 0x413c4, - 0x41400, 0x4141c, + 0x41400, 0x4140c, + 0x41414, 0x4141c, 0x41480, 0x414d0, - 0x44000, 0x44078, - 0x440c0, 0x44278, - 0x442c0, 0x44478, - 0x444c0, 0x44678, - 0x446c0, 0x44878, - 0x448c0, 0x449fc, - 0x45000, 0x45068, + 0x44000, 0x44054, + 0x4405c, 0x44078, + 0x440c0, 0x44174, + 0x44180, 0x441ac, + 0x441b4, 0x441b8, + 0x441c0, 0x44254, + 0x4425c, 0x44278, + 0x442c0, 0x44374, + 0x44380, 0x443ac, + 0x443b4, 0x443b8, + 0x443c0, 0x44454, + 0x4445c, 0x44478, + 0x444c0, 0x44574, + 0x44580, 0x445ac, + 0x445b4, 0x445b8, + 0x445c0, 0x44654, + 0x4465c, 0x44678, + 0x446c0, 0x44774, + 0x44780, 0x447ac, + 0x447b4, 0x447b8, + 0x447c0, 0x44854, + 0x4485c, 0x44878, + 0x448c0, 0x44974, + 0x44980, 0x449ac, + 0x449b4, 0x449b8, + 0x449c0, 0x449fc, + 0x45000, 0x45004, + 0x45010, 0x45030, + 0x45040, 0x45060, + 0x45068, 0x45068, 0x45080, 0x45084, 0x450a0, 0x450b0, - 0x45200, 0x45268, + 0x45200, 0x45204, + 0x45210, 0x45230, + 0x45240, 0x45260, + 0x45268, 0x45268, 0x45280, 0x45284, 0x452a0, 0x452b0, 0x460c0, 0x460e4, - 0x47000, 0x4708c, + 0x47000, 0x4703c, + 0x47044, 0x4708c, 0x47200, 0x47250, - 0x47400, 0x47420, + 0x47400, 0x47408, + 0x47414, 0x47420, 0x47600, 0x47618, 0x47800, 0x47814, 0x48000, 0x4800c, - 0x48040, 0x48068, - 0x4807c, 0x48144, + 0x48040, 0x48050, + 0x48060, 0x48068, + 0x4807c, 0x4808c, + 0x48094, 0x480b0, + 0x480c0, 0x48144, 0x48180, 0x4818c, - 0x48200, 0x48298, - 0x482ac, 0x4833c, + 0x48200, 0x48254, + 0x48260, 0x48264, + 0x48270, 0x48288, + 0x48290, 0x48298, + 0x482ac, 0x482c8, + 0x482d0, 0x482e0, + 0x482f0, 0x482f0, + 0x48300, 0x4833c, 0x483f8, 0x483fc, 0x49304, 0x493c4, - 0x49400, 0x4941c, + 0x49400, 0x4940c, + 0x49414, 0x4941c, 0x49480, 0x494d0, - 0x4c000, 0x4c078, - 0x4c0c0, 0x4c278, - 0x4c2c0, 0x4c478, - 0x4c4c0, 0x4c678, - 0x4c6c0, 0x4c878, - 0x4c8c0, 0x4c9fc, - 0x4d000, 0x4d068, + 0x4c000, 0x4c054, + 0x4c05c, 0x4c078, + 0x4c0c0, 0x4c174, + 0x4c180, 0x4c1ac, + 0x4c1b4, 0x4c1b8, + 0x4c1c0, 0x4c254, + 0x4c25c, 0x4c278, + 0x4c2c0, 0x4c374, + 0x4c380, 0x4c3ac, + 0x4c3b4, 0x4c3b8, + 0x4c3c0, 0x4c454, + 0x4c45c, 0x4c478, + 0x4c4c0, 0x4c574, + 0x4c580, 0x4c5ac, + 0x4c5b4, 0x4c5b8, + 0x4c5c0, 0x4c654, + 0x4c65c, 0x4c678, + 0x4c6c0, 0x4c774, + 0x4c780, 0x4c7ac, + 0x4c7b4, 0x4c7b8, + 0x4c7c0, 0x4c854, + 0x4c85c, 0x4c878, + 0x4c8c0, 0x4c974, + 0x4c980, 0x4c9ac, + 0x4c9b4, 0x4c9b8, + 0x4c9c0, 0x4c9fc, + 0x4d000, 0x4d004, + 0x4d010, 0x4d030, + 0x4d040, 0x4d060, + 0x4d068, 0x4d068, 0x4d080, 0x4d084, 0x4d0a0, 0x4d0b0, - 0x4d200, 0x4d268, + 0x4d200, 0x4d204, + 0x4d210, 0x4d230, + 0x4d240, 0x4d260, + 0x4d268, 0x4d268, 0x4d280, 0x4d284, 0x4d2a0, 0x4d2b0, 0x4e0c0, 0x4e0e4, - 0x4f000, 0x4f08c, + 0x4f000, 0x4f03c, + 0x4f044, 0x4f08c, 0x4f200, 0x4f250, - 0x4f400, 0x4f420, + 0x4f400, 0x4f408, + 0x4f414, 0x4f420, 0x4f600, 0x4f618, 0x4f800, 0x4f814, - 0x50000, 0x500cc, + 0x50000, 0x50084, + 0x50090, 0x500cc, 0x50400, 0x50400, - 0x50800, 0x508cc, + 0x50800, 0x50884, + 0x50890, 0x508cc, 0x50c00, 0x50c00, 0x51000, 0x5101c, 0x51300, 0x51308, }; static const unsigned int t6_reg_ranges[] = { - 0x1008, 0x1124, - 0x1138, 0x114c, - 0x1180, 0x11b4, + 0x1008, 0x101c, + 0x1024, 0x10a8, + 0x10b4, 0x10f8, + 0x1100, 0x1114, + 0x111c, 0x112c, + 0x1138, 0x113c, + 0x1144, 0x114c, + 0x1180, 0x1184, + 0x1190, 0x1194, + 0x11a0, 0x11a4, + 0x11b0, 0x11b4, 0x11fc, 0x1254, 0x1280, 0x133c, 0x1800, 0x18fc, 0x3000, 0x302c, - 0x3060, 0x30d8, + 0x3060, 0x30b0, + 0x30b8, 0x30d8, 0x30e0, 0x30fc, 0x3140, 0x357c, 0x35a8, 0x35cc, 0x35ec, 0x35ec, 0x3600, 0x5624, - 0x56cc, 0x575c, + 0x56cc, 0x56ec, + 0x56f4, 0x5720, + 0x5728, 0x575c, 0x580c, 0x5814, - 0x5890, 0x58bc, + 0x5890, 0x589c, + 0x58a4, 0x58ac, + 0x58b8, 0x58bc, 0x5940, 0x595c, 0x5980, 0x598c, - 0x59b0, 0x59dc, + 0x59b0, 0x59c8, + 0x59d0, 0x59dc, 0x59fc, 0x5a18, 0x5a60, 0x5a6c, - 0x5a80, 0x5a9c, + 0x5a80, 0x5a8c, + 0x5a94, 0x5a9c, 0x5b94, 0x5bfc, - 0x5c10, 0x5ec0, + 0x5c10, 0x5e48, + 0x5e50, 0x5e94, + 0x5ea0, 0x5eb0, + 0x5ec0, 0x5ec0, 0x5ec8, 0x5ecc, - 0x6000, 0x6040, - 0x6058, 0x619c, + 0x6000, 0x6020, + 0x6028, 0x6040, + 0x6058, 0x609c, + 0x60a8, 0x619c, 0x7700, 0x7798, 0x77c0, 0x7880, 0x78cc, 0x78fc, - 0x7b00, 0x7c54, - 0x7d00, 0x7efc, + 0x7b00, 0x7b58, + 0x7b60, 0x7b84, + 0x7b8c, 0x7c54, + 0x7d00, 0x7d38, + 0x7d40, 0x7d84, + 0x7d8c, 0x7ddc, + 0x7de4, 0x7e04, + 0x7e10, 0x7e1c, + 0x7e24, 0x7e38, + 0x7e40, 0x7e44, + 0x7e4c, 0x7e78, + 0x7e80, 0x7edc, + 0x7ee8, 0x7efc, 0x8dc0, 0x8de4, - 0x8df8, 0x8e84, + 0x8df8, 0x8e04, + 0x8e10, 0x8e84, 0x8ea0, 0x8f88, - 0x8fb8, 0x9124, + 0x8fb8, 0x9058, + 0x9060, 0x9060, + 0x9068, 0x90f8, + 0x9100, 0x9124, 0x9400, 0x9470, - 0x9600, 0x971c, + 0x9600, 0x9600, + 0x9608, 0x9638, + 0x9640, 0x9704, + 0x9710, 0x971c, 0x9800, 0x9808, 0x9820, 0x983c, 0x9850, 0x9864, @@ -1411,109 +2020,170 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x9f80, 0xa020, 0xd004, 0xd03c, 0xd100, 0xd118, - 0xd200, 0xd31c, + 0xd200, 0xd214, + 0xd220, 0xd234, + 0xd240, 0xd254, + 0xd260, 0xd274, + 0xd280, 0xd294, + 0xd2a0, 0xd2b4, + 0xd2c0, 0xd2d4, + 0xd2e0, 0xd2f4, + 0xd300, 0xd31c, 0xdfc0, 0xdfe0, 0xe000, 0xf008, 0x11000, 0x11014, - 0x11048, 0x1117c, - 0x11190, 0x11270, + 0x11048, 0x1106c, + 0x11074, 0x11088, + 0x11098, 0x11120, + 0x1112c, 0x1117c, + 0x11190, 0x112e0, 0x11300, 0x1130c, 0x12000, 0x1206c, 0x19040, 0x1906c, 0x19078, 0x19080, - 0x1908c, 0x19124, - 0x19150, 0x191b0, + 0x1908c, 0x190e8, + 0x190f0, 0x190f8, + 0x19100, 0x19110, + 0x19120, 0x19124, + 0x19150, 0x19194, + 0x1919c, 0x191b0, 0x191d0, 0x191e8, - 0x19238, 0x192bc, - 0x193f8, 0x19474, + 0x19238, 0x192b0, + 0x192bc, 0x192bc, + 0x19348, 0x1934c, + 0x193f8, 0x19418, + 0x19420, 0x19428, + 0x19430, 0x19444, + 0x1944c, 0x1946c, + 0x19474, 0x19474, 0x19490, 0x194cc, 0x194f0, 0x194f8, - 0x19c00, 0x19c80, - 0x19c94, 0x19cbc, - 0x19ce4, 0x19d28, + 0x19c00, 0x19c48, + 0x19c50, 0x19c80, + 0x19c94, 0x19c98, + 0x19ca0, 0x19cbc, + 0x19ce4, 0x19ce4, + 0x19cf0, 0x19cf8, + 0x19d00, 0x19d28, 0x19d50, 0x19d78, - 0x19d94, 0x19dc8, + 0x19d94, 0x19d98, + 0x19da0, 0x19dc8, 0x19df0, 0x19e10, 0x19e50, 0x19e6c, - 0x19ea0, 0x19f34, + 0x19ea0, 0x19ebc, + 0x19ec4, 0x19ef4, + 0x19f04, 0x19f2c, + 0x19f34, 0x19f34, 0x19f40, 0x19f50, 0x19f90, 0x19fac, - 0x19fc4, 0x19fe4, - 0x1a000, 0x1a06c, - 0x1a0b0, 0x1a120, - 0x1a128, 0x1a138, + 0x19fc4, 0x19fc8, + 0x19fd0, 0x19fe4, + 0x1a000, 0x1a004, + 0x1a010, 0x1a06c, + 0x1a0b0, 0x1a0e4, + 0x1a0ec, 0x1a0f8, + 0x1a100, 0x1a108, + 0x1a114, 0x1a120, + 0x1a128, 0x1a130, + 0x1a138, 0x1a138, 0x1a190, 0x1a1c4, 0x1a1fc, 0x1a1fc, 0x1e008, 0x1e00c, - 0x1e040, 0x1e04c, + 0x1e040, 0x1e044, + 0x1e04c, 0x1e04c, 0x1e284, 0x1e290, 0x1e2c0, 0x1e2c0, 0x1e2e0, 0x1e2e0, 0x1e300, 0x1e384, 0x1e3c0, 0x1e3c8, 0x1e408, 0x1e40c, - 0x1e440, 0x1e44c, + 0x1e440, 0x1e444, + 0x1e44c, 0x1e44c, 0x1e684, 0x1e690, 0x1e6c0, 0x1e6c0, 0x1e6e0, 0x1e6e0, 0x1e700, 0x1e784, 0x1e7c0, 0x1e7c8, 0x1e808, 0x1e80c, - 0x1e840, 0x1e84c, + 0x1e840, 0x1e844, + 0x1e84c, 0x1e84c, 0x1ea84, 0x1ea90, 0x1eac0, 0x1eac0, 0x1eae0, 0x1eae0, 0x1eb00, 0x1eb84, 0x1ebc0, 0x1ebc8, 0x1ec08, 0x1ec0c, - 0x1ec40, 0x1ec4c, + 0x1ec40, 0x1ec44, + 0x1ec4c, 0x1ec4c, 0x1ee84, 0x1ee90, 0x1eec0, 0x1eec0, 0x1eee0, 0x1eee0, 0x1ef00, 0x1ef84, 0x1efc0, 0x1efc8, 0x1f008, 0x1f00c, - 0x1f040, 0x1f04c, + 0x1f040, 0x1f044, + 0x1f04c, 0x1f04c, 0x1f284, 0x1f290, 0x1f2c0, 0x1f2c0, 0x1f2e0, 0x1f2e0, 0x1f300, 0x1f384, 0x1f3c0, 0x1f3c8, 0x1f408, 0x1f40c, - 0x1f440, 0x1f44c, + 0x1f440, 0x1f444, + 0x1f44c, 0x1f44c, 0x1f684, 0x1f690, 0x1f6c0, 0x1f6c0, 0x1f6e0, 0x1f6e0, 0x1f700, 0x1f784, 0x1f7c0, 0x1f7c8, 0x1f808, 0x1f80c, - 0x1f840, 0x1f84c, + 0x1f840, 0x1f844, + 0x1f84c, 0x1f84c, 0x1fa84, 0x1fa90, 0x1fac0, 0x1fac0, 0x1fae0, 0x1fae0, 0x1fb00, 0x1fb84, 0x1fbc0, 0x1fbc8, 0x1fc08, 0x1fc0c, - 0x1fc40, 0x1fc4c, + 0x1fc40, 0x1fc44, + 0x1fc4c, 0x1fc4c, 0x1fe84, 0x1fe90, 0x1fec0, 0x1fec0, 0x1fee0, 0x1fee0, 0x1ff00, 0x1ff84, 0x1ffc0, 0x1ffc8, - 0x30000, 0x30070, - 0x30100, 0x301d0, + 0x30000, 0x30030, + 0x30038, 0x30038, + 0x30040, 0x30040, + 0x30048, 0x30048, + 0x30050, 0x30050, + 0x3005c, 0x30060, + 0x30068, 0x30068, + 0x30070, 0x30070, + 0x30100, 0x30168, + 0x30190, 0x301a0, + 0x301a8, 0x301b8, + 0x301c4, 0x301c8, + 0x301d0, 0x301d0, 0x30200, 0x30320, - 0x30400, 0x3052c, + 0x30400, 0x304b4, + 0x304c0, 0x3052c, 0x30540, 0x3061c, - 0x30800, 0x30890, + 0x30800, 0x308a0, 0x308c0, 0x30908, 0x30910, 0x309b8, 0x30a00, 0x30a04, - 0x30a0c, 0x30a2c, + 0x30a0c, 0x30a14, + 0x30a1c, 0x30a2c, 0x30a44, 0x30a50, - 0x30a74, 0x30c24, - 0x30d00, 0x30d3c, - 0x30d44, 0x30d7c, + 0x30a74, 0x30a74, + 0x30a7c, 0x30afc, + 0x30b08, 0x30c24, + 0x30d00, 0x30d14, + 0x30d1c, 0x30d3c, + 0x30d44, 0x30d4c, + 0x30d54, 0x30d74, + 0x30d7c, 0x30d7c, 0x30de0, 0x30de0, 0x30e00, 0x30ed4, 0x30f00, 0x30fa4, @@ -1542,7 +2212,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x31bb0, 0x31bb4, 0x31bc8, 0x31bd4, 0x32140, 0x3218c, - 0x321f0, 0x32200, + 0x321f0, 0x321f4, + 0x32200, 0x32200, 0x32218, 0x32218, 0x32400, 0x32400, 0x32408, 0x3241c, @@ -1551,46 +2222,108 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x326a8, 0x326a8, 0x326ec, 0x326ec, 0x32a00, 0x32abc, - 0x32b00, 0x32b78, + 0x32b00, 0x32b38, + 0x32b40, 0x32b58, + 0x32b60, 0x32b78, 0x32c00, 0x32c00, 0x32c08, 0x32c3c, 0x32e00, 0x32e2c, 0x32f00, 0x32f2c, - 0x33000, 0x330ac, - 0x330c0, 0x331ac, - 0x331c0, 0x332c4, - 0x332e4, 0x333c4, - 0x333e4, 0x334ac, - 0x334c0, 0x335ac, - 0x335c0, 0x336c4, - 0x336e4, 0x337c4, + 0x33000, 0x3302c, + 0x33034, 0x33050, + 0x33058, 0x33058, + 0x33060, 0x3308c, + 0x3309c, 0x330ac, + 0x330c0, 0x330c0, + 0x330c8, 0x330d0, + 0x330d8, 0x330e0, + 0x330ec, 0x3312c, + 0x33134, 0x33150, + 0x33158, 0x33158, + 0x33160, 0x3318c, + 0x3319c, 0x331ac, + 0x331c0, 0x331c0, + 0x331c8, 0x331d0, + 0x331d8, 0x331e0, + 0x331ec, 0x33290, + 0x33298, 0x332c4, + 0x332e4, 0x33390, + 0x33398, 0x333c4, + 0x333e4, 0x3342c, + 0x33434, 0x33450, + 0x33458, 0x33458, + 0x33460, 0x3348c, + 0x3349c, 0x334ac, + 0x334c0, 0x334c0, + 0x334c8, 0x334d0, + 0x334d8, 0x334e0, + 0x334ec, 0x3352c, + 0x33534, 0x33550, + 0x33558, 0x33558, + 0x33560, 0x3358c, + 0x3359c, 0x335ac, + 0x335c0, 0x335c0, + 0x335c8, 0x335d0, + 0x335d8, 0x335e0, + 0x335ec, 0x33690, + 0x33698, 0x336c4, + 0x336e4, 0x33790, + 0x33798, 0x337c4, 0x337e4, 0x337fc, 0x33814, 0x33814, 0x33854, 0x33868, 0x33880, 0x3388c, 0x338c0, 0x338d0, 0x338e8, 0x338ec, - 0x33900, 0x339ac, - 0x339c0, 0x33ac4, + 0x33900, 0x3392c, + 0x33934, 0x33950, + 0x33958, 0x33958, + 0x33960, 0x3398c, + 0x3399c, 0x339ac, + 0x339c0, 0x339c0, + 0x339c8, 0x339d0, + 0x339d8, 0x339e0, + 0x339ec, 0x33a90, + 0x33a98, 0x33ac4, 0x33ae4, 0x33b10, - 0x33b24, 0x33b50, + 0x33b24, 0x33b28, + 0x33b38, 0x33b50, 0x33bf0, 0x33c10, - 0x33c24, 0x33c50, + 0x33c24, 0x33c28, + 0x33c38, 0x33c50, 0x33cf0, 0x33cfc, - 0x34000, 0x34070, - 0x34100, 0x341d0, + 0x34000, 0x34030, + 0x34038, 0x34038, + 0x34040, 0x34040, + 0x34048, 0x34048, + 0x34050, 0x34050, + 0x3405c, 0x34060, + 0x34068, 0x34068, + 0x34070, 0x34070, + 0x34100, 0x34168, + 0x34190, 0x341a0, + 0x341a8, 0x341b8, + 0x341c4, 0x341c8, + 0x341d0, 0x341d0, 0x34200, 0x34320, - 0x34400, 0x3452c, + 0x34400, 0x344b4, + 0x344c0, 0x3452c, 0x34540, 0x3461c, - 0x34800, 0x34890, + 0x34800, 0x348a0, 0x348c0, 0x34908, 0x34910, 0x349b8, 0x34a00, 0x34a04, - 0x34a0c, 0x34a2c, + 0x34a0c, 0x34a14, + 0x34a1c, 0x34a2c, 0x34a44, 0x34a50, - 0x34a74, 0x34c24, - 0x34d00, 0x34d3c, - 0x34d44, 0x34d7c, + 0x34a74, 0x34a74, + 0x34a7c, 0x34afc, + 0x34b08, 0x34c24, + 0x34d00, 0x34d14, + 0x34d1c, 0x34d3c, + 0x34d44, 0x34d4c, + 0x34d54, 0x34d74, + 0x34d7c, 0x34d7c, 0x34de0, 0x34de0, 0x34e00, 0x34ed4, 0x34f00, 0x34fa4, @@ -1619,7 +2352,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x35bb0, 0x35bb4, 0x35bc8, 0x35bd4, 0x36140, 0x3618c, - 0x361f0, 0x36200, + 0x361f0, 0x361f4, + 0x36200, 0x36200, 0x36218, 0x36218, 0x36400, 0x36400, 0x36408, 0x3641c, @@ -1628,31 +2362,75 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x366a8, 0x366a8, 0x366ec, 0x366ec, 0x36a00, 0x36abc, - 0x36b00, 0x36b78, + 0x36b00, 0x36b38, + 0x36b40, 0x36b58, + 0x36b60, 0x36b78, 0x36c00, 0x36c00, 0x36c08, 0x36c3c, 0x36e00, 0x36e2c, 0x36f00, 0x36f2c, - 0x37000, 0x370ac, - 0x370c0, 0x371ac, - 0x371c0, 0x372c4, - 0x372e4, 0x373c4, - 0x373e4, 0x374ac, - 0x374c0, 0x375ac, - 0x375c0, 0x376c4, - 0x376e4, 0x377c4, + 0x37000, 0x3702c, + 0x37034, 0x37050, + 0x37058, 0x37058, + 0x37060, 0x3708c, + 0x3709c, 0x370ac, + 0x370c0, 0x370c0, + 0x370c8, 0x370d0, + 0x370d8, 0x370e0, + 0x370ec, 0x3712c, + 0x37134, 0x37150, + 0x37158, 0x37158, + 0x37160, 0x3718c, + 0x3719c, 0x371ac, + 0x371c0, 0x371c0, + 0x371c8, 0x371d0, + 0x371d8, 0x371e0, + 0x371ec, 0x37290, + 0x37298, 0x372c4, + 0x372e4, 0x37390, + 0x37398, 0x373c4, + 0x373e4, 0x3742c, + 0x37434, 0x37450, + 0x37458, 0x37458, + 0x37460, 0x3748c, + 0x3749c, 0x374ac, + 0x374c0, 0x374c0, + 0x374c8, 0x374d0, + 0x374d8, 0x374e0, + 0x374ec, 0x3752c, + 0x37534, 0x37550, + 0x37558, 0x37558, + 0x37560, 0x3758c, + 0x3759c, 0x375ac, + 0x375c0, 0x375c0, + 0x375c8, 0x375d0, + 0x375d8, 0x375e0, + 0x375ec, 0x37690, + 0x37698, 0x376c4, + 0x376e4, 0x37790, + 0x37798, 0x377c4, 0x377e4, 0x377fc, 0x37814, 0x37814, 0x37854, 0x37868, 0x37880, 0x3788c, 0x378c0, 0x378d0, 0x378e8, 0x378ec, - 0x37900, 0x379ac, - 0x379c0, 0x37ac4, + 0x37900, 0x3792c, + 0x37934, 0x37950, + 0x37958, 0x37958, + 0x37960, 0x3798c, + 0x3799c, 0x379ac, + 0x379c0, 0x379c0, + 0x379c8, 0x379d0, + 0x379d8, 0x379e0, + 0x379ec, 0x37a90, + 0x37a98, 0x37ac4, 0x37ae4, 0x37b10, - 0x37b24, 0x37b50, + 0x37b24, 0x37b28, + 0x37b38, 0x37b50, 0x37bf0, 0x37c10, - 0x37c24, 0x37c50, + 0x37c24, 0x37c28, + 0x37c38, 0x37c50, 0x37cf0, 0x37cfc, 0x40040, 0x40040, 0x40080, 0x40084, @@ -1664,36 +2442,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x40280, 0x40280, 0x40304, 0x40304, 0x40330, 0x4033c, - 0x41304, 0x413dc, - 0x41400, 0x4141c, + 0x41304, 0x413c8, + 0x413d0, 0x413dc, + 0x413f0, 0x413f0, + 0x41400, 0x4140c, + 0x41414, 0x4141c, 0x41480, 0x414d0, 0x44000, 0x4407c, - 0x440c0, 0x4427c, - 0x442c0, 0x4447c, - 0x444c0, 0x4467c, - 0x446c0, 0x4487c, - 0x448c0, 0x44a7c, - 0x44ac0, 0x44c7c, - 0x44cc0, 0x44e7c, - 0x44ec0, 0x4507c, - 0x450c0, 0x451fc, - 0x45800, 0x45868, + 0x440c0, 0x441ac, + 0x441b4, 0x4427c, + 0x442c0, 0x443ac, + 0x443b4, 0x4447c, + 0x444c0, 0x445ac, + 0x445b4, 0x4467c, + 0x446c0, 0x447ac, + 0x447b4, 0x4487c, + 0x448c0, 0x449ac, + 0x449b4, 0x44a7c, + 0x44ac0, 0x44bac, + 0x44bb4, 0x44c7c, + 0x44cc0, 0x44dac, + 0x44db4, 0x44e7c, + 0x44ec0, 0x44fac, + 0x44fb4, 0x4507c, + 0x450c0, 0x451ac, + 0x451b4, 0x451fc, + 0x45800, 0x45804, + 0x45810, 0x45830, + 0x45840, 0x45860, + 0x45868, 0x45868, 0x45880, 0x45884, 0x458a0, 0x458b0, - 0x45a00, 0x45a68, + 0x45a00, 0x45a04, + 0x45a10, 0x45a30, + 0x45a40, 0x45a60, + 0x45a68, 0x45a68, 0x45a80, 0x45a84, 0x45aa0, 0x45ab0, 0x460c0, 0x460e4, - 0x47000, 0x4708c, + 0x47000, 0x4703c, + 0x47044, 0x4708c, 0x47200, 0x47250, - 0x47400, 0x47420, + 0x47400, 0x47408, + 0x47414, 0x47420, 0x47600, 0x47618, - 0x47800, 0x4782c, - 0x50000, 0x500cc, + 0x47800, 0x47814, + 0x47820, 0x4782c, + 0x50000, 0x50084, + 0x50090, 0x500cc, + 0x50300, 0x50384, 0x50400, 0x50400, - 0x50800, 0x508cc, + 0x50800, 0x50884, + 0x50890, 0x508cc, + 0x50b00, 0x50b84, 0x50c00, 0x50c00, - 0x51000, 0x510b0, + 0x51000, 0x51020, + 0x51028, 0x510b0, 0x51300, 0x51324, }; @@ -2177,11 +2981,15 @@ int t4_get_exprom_version(struct adapter *adap, u32 *vers) */ int t4_check_fw_version(struct adapter *adap) { - int ret, major, minor, micro; + int i, ret, major, minor, micro; int exp_major, exp_minor, exp_micro; unsigned int chip_version = CHELSIO_CHIP_VERSION(adap->params.chip); ret = t4_get_fw_version(adap, &adap->params.fw_vers); + /* Try multiple times before returning error */ + for (i = 0; (ret == -EBUSY || ret == -EAGAIN) && i < 3; i++) + ret = t4_get_fw_version(adap, &adap->params.fw_vers); + if (ret) return ret; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h index 640369df8b3a..13708fde1668 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h @@ -263,4 +263,9 @@ enum { #undef FLASH_START #undef FLASH_MAX_SIZE +#define SGE_TIMESTAMP_S 0 +#define SGE_TIMESTAMP_M 0xfffffffffffffffULL +#define SGE_TIMESTAMP_V(x) ((__u64)(x) << SGE_TIMESTAMP_S) +#define SGE_TIMESTAMP_G(x) (((__u64)(x) >> SGE_TIMESTAMP_S) & SGE_TIMESTAMP_M) + #endif /* __T4_HW_H */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h index 8353a6cbfcc2..03ed00c49823 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h @@ -157,6 +157,11 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN CH_PCI_ID_TABLE_FENTRY(0x5090), /* Custom T540-CR */ CH_PCI_ID_TABLE_FENTRY(0x5091), /* Custom T522-CR */ CH_PCI_ID_TABLE_FENTRY(0x5092), /* Custom T520-CR */ + CH_PCI_ID_TABLE_FENTRY(0x5093), /* Custom T580-LP-CR */ + CH_PCI_ID_TABLE_FENTRY(0x5094), /* Custom T540-CR */ + CH_PCI_ID_TABLE_FENTRY(0x5095), /* Custom T540-CR-SO */ + CH_PCI_ID_TABLE_FENTRY(0x5096), /* Custom T580-CR */ + CH_PCI_ID_TABLE_FENTRY(0x5097), /* Custom T520-KR */ /* T6 adapters: */ diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index 8b53f7d4bebf..6401ba99457f 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -143,6 +143,7 @@ struct enic { struct vnic_dev *vdev; struct timer_list notify_timer; struct work_struct reset; + struct work_struct tx_hang_reset; struct work_struct change_mtu_work; struct msix_entry msix_entry[ENIC_INTR_MAX]; struct enic_msix_entry msix[ENIC_INTR_MAX]; diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 3352d027ab89..0c22fd014378 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -178,13 +178,15 @@ static int enic_wq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc, return 0; } -static void enic_log_q_error(struct enic *enic) +static bool enic_log_q_error(struct enic *enic) { unsigned int i; u32 error_status; + bool err = false; for (i = 0; i < enic->wq_count; i++) { error_status = vnic_wq_error_status(&enic->wq[i]); + err |= error_status; if (error_status) netdev_err(enic->netdev, "WQ[%d] error_status %d\n", i, error_status); @@ -192,10 +194,13 @@ static void enic_log_q_error(struct enic *enic) for (i = 0; i < enic->rq_count; i++) { error_status = vnic_rq_error_status(&enic->rq[i]); + err |= error_status; if (error_status) netdev_err(enic->netdev, "RQ[%d] error_status %d\n", i, error_status); } + + return err; } static void enic_msglvl_check(struct enic *enic) @@ -333,10 +338,9 @@ static irqreturn_t enic_isr_msix_err(int irq, void *data) vnic_intr_return_all_credits(&enic->intr[intr]); - enic_log_q_error(enic); - - /* schedule recovery from WQ/RQ error */ - schedule_work(&enic->reset); + if (enic_log_q_error(enic)) + /* schedule recovery from WQ/RQ error */ + schedule_work(&enic->reset); return IRQ_HANDLED; } @@ -804,7 +808,7 @@ static void enic_set_rx_mode(struct net_device *netdev) static void enic_tx_timeout(struct net_device *netdev) { struct enic *enic = netdev_priv(netdev); - schedule_work(&enic->reset); + schedule_work(&enic->tx_hang_reset); } static int enic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) @@ -1924,6 +1928,19 @@ static int enic_dev_open(struct enic *enic) return err; } +static int enic_dev_soft_reset(struct enic *enic) +{ + int err; + + err = enic_dev_wait(enic->vdev, vnic_dev_soft_reset, + vnic_dev_soft_reset_done, 0); + if (err) + netdev_err(enic->netdev, "vNIC soft reset failed, err %d\n", + err); + + return err; +} + static int enic_dev_hang_reset(struct enic *enic) { int err; @@ -2060,6 +2077,26 @@ static void enic_reset(struct work_struct *work) rtnl_lock(); spin_lock(&enic->enic_api_lock); + enic_stop(enic->netdev); + enic_dev_soft_reset(enic); + enic_reset_addr_lists(enic); + enic_init_vnic_resources(enic); + enic_set_rss_nic_cfg(enic); + enic_dev_set_ig_vlan_rewrite_mode(enic); + enic_open(enic->netdev); + spin_unlock(&enic->enic_api_lock); + call_netdevice_notifiers(NETDEV_REBOOT, enic->netdev); + + rtnl_unlock(); +} + +static void enic_tx_hang_reset(struct work_struct *work) +{ + struct enic *enic = container_of(work, struct enic, tx_hang_reset); + + rtnl_lock(); + + spin_lock(&enic->enic_api_lock); enic_dev_hang_notify(enic); enic_stop(enic->netdev); enic_dev_hang_reset(enic); @@ -2583,6 +2620,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) enic_set_rx_coal_setting(enic); INIT_WORK(&enic->reset, enic_reset); + INIT_WORK(&enic->tx_hang_reset, enic_tx_hang_reset); INIT_WORK(&enic->change_mtu_work, enic_change_mtu_work); for (i = 0; i < enic->wq_count; i++) diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c index a3badefaf360..1ffd1050860b 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.c +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c @@ -659,14 +659,14 @@ int vnic_dev_open_done(struct vnic_dev *vdev, int *done) return 0; } -static int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg) +int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg) { u64 a0 = (u32)arg, a1 = 0; int wait = 1000; return vnic_dev_cmd(vdev, CMD_SOFT_RESET, &a0, &a1, wait); } -static int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) +int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) { u64 a0 = 0, a1 = 0; int wait = 1000; diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.h b/drivers/net/ethernet/cisco/enic/vnic_dev.h index b013b6a78e87..54156c484424 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.h +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.h @@ -155,7 +155,9 @@ int vnic_dev_deinit(struct vnic_dev *vdev); void vnic_dev_intr_coal_timer_info_default(struct vnic_dev *vdev); int vnic_dev_intr_coal_timer_info(struct vnic_dev *vdev); int vnic_dev_hang_reset(struct vnic_dev *vdev, int arg); +int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg); int vnic_dev_hang_reset_done(struct vnic_dev *vdev, int *done); +int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done); void vnic_dev_set_intr_mode(struct vnic_dev *vdev, enum vnic_dev_intr_mode intr_mode); enum vnic_dev_intr_mode vnic_dev_get_intr_mode(struct vnic_dev *vdev); diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 0a27805cbbbd..821540913343 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -582,6 +582,7 @@ struct be_adapter { u16 pvid; __be16 vxlan_port; int vxlan_port_count; + int vxlan_port_aliases; struct phy_info phy; u8 wol_cap; bool wol_en; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 12687bf52b95..7bf51a1a0a77 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -5176,6 +5176,11 @@ static void be_add_vxlan_port(struct net_device *netdev, sa_family_t sa_family, if (lancer_chip(adapter) || BEx_chip(adapter) || be_is_mc(adapter)) return; + if (adapter->vxlan_port == port && adapter->vxlan_port_count) { + adapter->vxlan_port_aliases++; + return; + } + if (adapter->flags & BE_FLAGS_VXLAN_OFFLOADS) { dev_info(dev, "Only one UDP port supported for VxLAN offloads\n"); @@ -5226,6 +5231,11 @@ static void be_del_vxlan_port(struct net_device *netdev, sa_family_t sa_family, if (adapter->vxlan_port != port) goto done; + if (adapter->vxlan_port_aliases) { + adapter->vxlan_port_aliases--; + return; + } + be_disable_vxlan_offloads(adapter); dev_info(&adapter->pdev->dev, diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index a2c96fd88393..ff665493ca97 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -201,6 +201,7 @@ struct ethoc { void __iomem *membase; int dma_alloc; resource_size_t io_region_size; + bool big_endian; unsigned int num_bd; unsigned int num_tx; @@ -236,12 +237,18 @@ struct ethoc_bd { static inline u32 ethoc_read(struct ethoc *dev, loff_t offset) { - return ioread32(dev->iobase + offset); + if (dev->big_endian) + return ioread32be(dev->iobase + offset); + else + return ioread32(dev->iobase + offset); } static inline void ethoc_write(struct ethoc *dev, loff_t offset, u32 data) { - iowrite32(data, dev->iobase + offset); + if (dev->big_endian) + iowrite32be(data, dev->iobase + offset); + else + iowrite32(data, dev->iobase + offset); } static inline void ethoc_read_bd(struct ethoc *dev, int index, @@ -1106,6 +1113,9 @@ static int ethoc_probe(struct platform_device *pdev) priv->dma_alloc = buffer_size; } + priv->big_endian = pdata ? pdata->big_endian : + of_device_is_big_endian(pdev->dev.of_node); + /* calculate the number of TX/RX buffers, maximum 128 supported */ num_bd = min_t(unsigned int, 128, (netdev->mem_end - netdev->mem_start + 1) / ETHOC_BUFSIZ); diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index dd4ca39d5d8f..501e1438d153 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3070,7 +3070,6 @@ static void fec_poll_controller(struct net_device *dev) } #endif -#define FEATURES_NEED_QUIESCE NETIF_F_RXCSUM static inline void fec_enet_set_netdev_features(struct net_device *netdev, netdev_features_t features) { @@ -3094,7 +3093,7 @@ static int fec_set_features(struct net_device *netdev, struct fec_enet_private *fep = netdev_priv(netdev); netdev_features_t changed = features ^ netdev->features; - if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) { + if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { napi_disable(&fep->napi); netif_tx_lock_bh(netdev); fec_stop(netdev); diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 1543cf0e8ef6..f9e74461bdc0 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -112,9 +112,8 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) unsigned long flags; u32 val, tempval; int inc; - struct timespec ts; + struct timespec64 ts; u64 ns; - u32 remainder; val = 0; if (!(fep->hwts_tx_en || fep->hwts_rx_en)) { @@ -163,8 +162,7 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) tempval = readl(fep->hwp + FEC_ATIME); /* Convert the ptp local counter to 1588 timestamp */ ns = timecounter_cyc2time(&fep->tc, tempval); - ts.tv_sec = div_u64_rem(ns, 1000000000ULL, &remainder); - ts.tv_nsec = remainder; + ts = ns_to_timespec64(ns); /* The tempval is less than 3 seconds, and so val is less than * 4 seconds. No overflow for 32bit calculation. diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 4b69d061d90f..7f5389c3c0cf 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -907,6 +907,9 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) if (of_find_property(np, "fsl,magic-packet", NULL)) priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET; + if (of_get_property(np, "fsl,wake-on-filer", NULL)) + priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER; + priv->phy_node = of_parse_phandle(np, "phy-handle", 0); /* In the case of a fixed PHY, the DT node associated @@ -1415,8 +1418,14 @@ static int gfar_probe(struct platform_device *ofdev) goto register_fail; } - device_set_wakeup_capable(&dev->dev, priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) + priv->wol_supported |= GFAR_WOL_MAGIC; + + if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER) && + priv->rx_filer_enable) + priv->wol_supported |= GFAR_WOL_FILER_UCAST; + + device_set_wakeup_capable(&ofdev->dev, priv->wol_supported); /* fill out IRQ number and name fields */ for (i = 0; i < priv->num_grps; i++) { @@ -1479,15 +1488,122 @@ static int gfar_remove(struct platform_device *ofdev) #ifdef CONFIG_PM +static void __gfar_filer_disable(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 temp; + + temp = gfar_read(®s->rctrl); + temp &= ~(RCTRL_FILREN | RCTRL_PRSDEP_INIT); + gfar_write(®s->rctrl, temp); +} + +static void __gfar_filer_enable(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 temp; + + temp = gfar_read(®s->rctrl); + temp |= RCTRL_FILREN | RCTRL_PRSDEP_INIT; + gfar_write(®s->rctrl, temp); +} + +/* Filer rules implementing wol capabilities */ +static void gfar_filer_config_wol(struct gfar_private *priv) +{ + unsigned int i; + u32 rqfcr; + + __gfar_filer_disable(priv); + + /* clear the filer table, reject any packet by default */ + rqfcr = RQFCR_RJE | RQFCR_CMP_MATCH; + for (i = 0; i <= MAX_FILER_IDX; i++) + gfar_write_filer(priv, i, rqfcr, 0); + + i = 0; + if (priv->wol_opts & GFAR_WOL_FILER_UCAST) { + /* unicast packet, accept it */ + struct net_device *ndev = priv->ndev; + /* get the default rx queue index */ + u8 qindex = (u8)priv->gfargrp[0].rx_queue->qindex; + u32 dest_mac_addr = (ndev->dev_addr[0] << 16) | + (ndev->dev_addr[1] << 8) | + ndev->dev_addr[2]; + + rqfcr = (qindex << 10) | RQFCR_AND | + RQFCR_CMP_EXACT | RQFCR_PID_DAH; + + gfar_write_filer(priv, i++, rqfcr, dest_mac_addr); + + dest_mac_addr = (ndev->dev_addr[3] << 16) | + (ndev->dev_addr[4] << 8) | + ndev->dev_addr[5]; + rqfcr = (qindex << 10) | RQFCR_GPI | + RQFCR_CMP_EXACT | RQFCR_PID_DAL; + gfar_write_filer(priv, i++, rqfcr, dest_mac_addr); + } + + __gfar_filer_enable(priv); +} + +static void gfar_filer_restore_table(struct gfar_private *priv) +{ + u32 rqfcr, rqfpr; + unsigned int i; + + __gfar_filer_disable(priv); + + for (i = 0; i <= MAX_FILER_IDX; i++) { + rqfcr = priv->ftp_rqfcr[i]; + rqfpr = priv->ftp_rqfpr[i]; + gfar_write_filer(priv, i, rqfcr, rqfpr); + } + + __gfar_filer_enable(priv); +} + +/* gfar_start() for Rx only and with the FGPI filer interrupt enabled */ +static void gfar_start_wol_filer(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 tempval; + int i = 0; + + /* Enable Rx hw queues */ + gfar_write(®s->rqueue, priv->rqueue); + + /* Initialize DMACTRL to have WWR and WOP */ + tempval = gfar_read(®s->dmactrl); + tempval |= DMACTRL_INIT_SETTINGS; + gfar_write(®s->dmactrl, tempval); + + /* Make sure we aren't stopped */ + tempval = gfar_read(®s->dmactrl); + tempval &= ~DMACTRL_GRS; + gfar_write(®s->dmactrl, tempval); + + for (i = 0; i < priv->num_grps; i++) { + regs = priv->gfargrp[i].regs; + /* Clear RHLT, so that the DMA starts polling now */ + gfar_write(®s->rstat, priv->gfargrp[i].rstat); + /* enable the Filer General Purpose Interrupt */ + gfar_write(®s->imask, IMASK_FGPI); + } + + /* Enable Rx DMA */ + tempval = gfar_read(®s->maccfg1); + tempval |= MACCFG1_RX_EN; + gfar_write(®s->maccfg1, tempval); +} + static int gfar_suspend(struct device *dev) { struct gfar_private *priv = dev_get_drvdata(dev); struct net_device *ndev = priv->ndev; struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 tempval; - int magic_packet = priv->wol_en && - (priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + u16 wol = priv->wol_opts; if (!netif_running(ndev)) return 0; @@ -1499,7 +1615,7 @@ static int gfar_suspend(struct device *dev) gfar_halt(priv); - if (magic_packet) { + if (wol & GFAR_WOL_MAGIC) { /* Enable interrupt on Magic Packet */ gfar_write(®s->imask, IMASK_MAG); @@ -1513,6 +1629,10 @@ static int gfar_suspend(struct device *dev) tempval |= MACCFG1_RX_EN; gfar_write(®s->maccfg1, tempval); + } else if (wol & GFAR_WOL_FILER_UCAST) { + gfar_filer_config_wol(priv); + gfar_start_wol_filer(priv); + } else { phy_stop(priv->phydev); } @@ -1526,18 +1646,22 @@ static int gfar_resume(struct device *dev) struct net_device *ndev = priv->ndev; struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 tempval; - int magic_packet = priv->wol_en && - (priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + u16 wol = priv->wol_opts; if (!netif_running(ndev)) return 0; - if (magic_packet) { + if (wol & GFAR_WOL_MAGIC) { /* Disable Magic Packet mode */ tempval = gfar_read(®s->maccfg2); tempval &= ~MACCFG2_MPEN; gfar_write(®s->maccfg2, tempval); + + } else if (wol & GFAR_WOL_FILER_UCAST) { + /* need to stop rx only, tx is already down */ + gfar_halt(priv); + gfar_filer_restore_table(priv); + } else { phy_start(priv->phydev); } @@ -1710,8 +1834,10 @@ static void gfar_configure_serdes(struct net_device *dev) * everything for us? Resetting it takes the link down and requires * several seconds for it to come back. */ - if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) + if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) { + put_device(&tbiphy->dev); return; + } /* Single clk mode, mii mode off(for serdes communication) */ phy_write(tbiphy, MII_TBICON, TBICON_CLK_SELECT); @@ -1723,6 +1849,8 @@ static void gfar_configure_serdes(struct net_device *dev) phy_write(tbiphy, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000); + + put_device(&tbiphy->dev); } static int __gfar_is_rx_idle(struct gfar_private *priv) @@ -1970,8 +2098,7 @@ static int register_grp_irqs(struct gfar_priv_grp *grp) /* Install our interrupt handlers for Error, * Transmit, and Receive */ - err = request_irq(gfar_irq(grp, ER)->irq, gfar_error, - IRQF_NO_SUSPEND, + err = request_irq(gfar_irq(grp, ER)->irq, gfar_error, 0, gfar_irq(grp, ER)->name, grp); if (err < 0) { netif_err(priv, intr, dev, "Can't get IRQ %d\n", @@ -1979,6 +2106,8 @@ static int register_grp_irqs(struct gfar_priv_grp *grp) goto err_irq_fail; } + enable_irq_wake(gfar_irq(grp, ER)->irq); + err = request_irq(gfar_irq(grp, TX)->irq, gfar_transmit, 0, gfar_irq(grp, TX)->name, grp); if (err < 0) { @@ -1993,15 +2122,17 @@ static int register_grp_irqs(struct gfar_priv_grp *grp) gfar_irq(grp, RX)->irq); goto rx_irq_fail; } + enable_irq_wake(gfar_irq(grp, RX)->irq); + } else { - err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt, - IRQF_NO_SUSPEND, + err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt, 0, gfar_irq(grp, TX)->name, grp); if (err < 0) { netif_err(priv, intr, dev, "Can't get IRQ %d\n", gfar_irq(grp, TX)->irq); goto err_irq_fail; } + enable_irq_wake(gfar_irq(grp, TX)->irq); } return 0; @@ -2738,7 +2869,14 @@ irqreturn_t gfar_receive(int irq, void *grp_id) { struct gfar_priv_grp *grp = (struct gfar_priv_grp *)grp_id; unsigned long flags; - u32 imask; + u32 imask, ievent; + + ievent = gfar_read(&grp->regs->ievent); + + if (unlikely(ievent & IEVENT_FGPI)) { + gfar_write(&grp->regs->ievent, IEVENT_FGPI); + return IRQ_HANDLED; + } if (likely(napi_schedule_prep(&grp->napi_rx))) { spin_lock_irqsave(&grp->grplock, flags); diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index 8c1994856e93..f266b20f9ef5 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h @@ -340,6 +340,7 @@ extern const char gfar_driver_version[]; #define IEVENT_MAG 0x00000800 #define IEVENT_GRSC 0x00000100 #define IEVENT_RXF0 0x00000080 +#define IEVENT_FGPI 0x00000010 #define IEVENT_FIR 0x00000008 #define IEVENT_FIQ 0x00000004 #define IEVENT_DPE 0x00000002 @@ -372,6 +373,7 @@ extern const char gfar_driver_version[]; #define IMASK_MAG 0x00000800 #define IMASK_GRSC 0x00000100 #define IMASK_RXFEN0 0x00000080 +#define IMASK_FGPI 0x00000010 #define IMASK_FIR 0x00000008 #define IMASK_FIQ 0x00000004 #define IMASK_DPE 0x00000002 @@ -540,6 +542,9 @@ extern const char gfar_driver_version[]; #define GFAR_INT_NAME_MAX (IFNAMSIZ + 6) /* '_g#_xx' */ +#define GFAR_WOL_MAGIC 0x00000001 +#define GFAR_WOL_FILER_UCAST 0x00000002 + struct txbd8 { union { @@ -917,6 +922,7 @@ struct gfar { #define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200 #define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400 #define FSL_GIANFAR_DEV_HAS_TIMER 0x00000800 +#define FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER 0x00001000 #if (MAXGROUPS == 2) #define DEFAULT_MAPPING 0xAA @@ -1161,8 +1167,6 @@ struct gfar_private { extended_hash:1, bd_stash_en:1, rx_filer_enable:1, - /* Wake-on-LAN enabled */ - wol_en:1, /* Enable priorty based Tx scheduling in Hw */ prio_sched_en:1, /* Flow control flags */ @@ -1191,6 +1195,10 @@ struct gfar_private { u32 __iomem *hash_regs[16]; int hash_width; + /* wake-on-lan settings */ + u16 wol_opts; + u16 wol_supported; + /*Filer table*/ unsigned int ftp_rqfpr[MAX_FILER_IDX + 1]; unsigned int ftp_rqfcr[MAX_FILER_IDX + 1]; diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 6bdc89179b72..fb7f8d67aef4 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -644,28 +644,49 @@ static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct gfar_private *priv = netdev_priv(dev); - if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) { - wol->supported = WAKE_MAGIC; - wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0; - } else { - wol->supported = wol->wolopts = 0; - } + wol->supported = 0; + wol->wolopts = 0; + + if (priv->wol_supported & GFAR_WOL_MAGIC) + wol->supported |= WAKE_MAGIC; + + if (priv->wol_supported & GFAR_WOL_FILER_UCAST) + wol->supported |= WAKE_UCAST; + + if (priv->wol_opts & GFAR_WOL_MAGIC) + wol->wolopts |= WAKE_MAGIC; + + if (priv->wol_opts & GFAR_WOL_FILER_UCAST) + wol->wolopts |= WAKE_UCAST; } static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct gfar_private *priv = netdev_priv(dev); + u16 wol_opts = 0; + int err; - if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && - wol->wolopts != 0) + if (!priv->wol_supported && wol->wolopts) return -EINVAL; - if (wol->wolopts & ~WAKE_MAGIC) + if (wol->wolopts & ~(WAKE_MAGIC | WAKE_UCAST)) return -EINVAL; - device_set_wakeup_enable(&dev->dev, wol->wolopts & WAKE_MAGIC); + if (wol->wolopts & WAKE_MAGIC) { + wol_opts |= GFAR_WOL_MAGIC; + } else { + if (wol->wolopts & WAKE_UCAST) + wol_opts |= GFAR_WOL_FILER_UCAST; + } + + wol_opts &= priv->wol_supported; + priv->wol_opts = 0; + + err = device_set_wakeup_enable(priv->dev, wol_opts); + if (err) + return err; - priv->wol_en = !!device_may_wakeup(&dev->dev); + priv->wol_opts = wol_opts; return 0; } diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c index 8e3cd77aa347..664d0c261269 100644 --- a/drivers/net/ethernet/freescale/gianfar_ptp.c +++ b/drivers/net/ethernet/freescale/gianfar_ptp.c @@ -557,6 +557,7 @@ static const struct of_device_id match_table[] = { { .compatible = "fsl,etsec-ptp" }, {}, }; +MODULE_DEVICE_TABLE(of, match_table); static struct platform_driver gianfar_ptp_driver = { .driver = { diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 4dd40e057f40..650f7888e32b 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -1384,6 +1384,8 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth) value = phy_read(tbiphy, ENET_TBI_MII_CR); value &= ~0x1000; /* Turn off autonegotiation */ phy_write(tbiphy, ENET_TBI_MII_CR, value); + + put_device(&tbiphy->dev); } init_check_frame_length_mode(ug_info->lengthCheckRx, &ug_regs->maccfg2); @@ -1702,8 +1704,10 @@ static void uec_configure_serdes(struct net_device *dev) * everything for us? Resetting it takes the link down and requires * several seconds for it to come back. */ - if (phy_read(tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS) + if (phy_read(tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS) { + put_device(&tbiphy->dev); return; + } /* Single clk mode, mii mode off(for serdes communication) */ phy_write(tbiphy, ENET_TBI_MII_ANA, TBIANA_SETTINGS); @@ -1711,6 +1715,8 @@ static void uec_configure_serdes(struct net_device *dev) phy_write(tbiphy, ENET_TBI_MII_TBICON, TBICON_CLK_SELECT); phy_write(tbiphy, ENET_TBI_MII_CR, TBICR_SETTINGS); + + put_device(&tbiphy->dev); } /* Configure the PHY for dev. diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig index dead17b5d769..8d12b587809e 100644 --- a/drivers/net/ethernet/hisilicon/Kconfig +++ b/drivers/net/ethernet/hisilicon/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_HISILICON bool "Hisilicon devices" default y - depends on ARM + depends on ARM || ARM64 ---help--- If you have a network (Ethernet) card belonging to this class, say Y. @@ -24,11 +24,42 @@ config HIX5HD2_GMAC config HIP04_ETH tristate "HISILICON P04 Ethernet support" - select PHYLIB select MARVELL_PHY select MFD_SYSCON + select HNS_MDIO ---help--- If you wish to compile a kernel for a hardware with hisilicon p04 SoC and want to use the internal ethernet then you should answer Y to this. +config HNS_MDIO + tristate + select PHYLIB + ---help--- + This selects the HNS MDIO support. It is needed by HNS_DSAF to access + the PHY + +config HNS + tristate "Hisilicon Network Subsystem Support (Framework)" + ---help--- + This selects the framework support for Hisilicon Network Subsystem. It + is needed by any driver which provides HNS acceleration engine or make + use of the engine + +config HNS_DSAF + tristate "Hisilicon HNS DSAF device Support" + select HNS + select HNS_MDIO + ---help--- + This selects the DSAF (Distributed System Area Frabric) network + acceleration engine support. The engine is used in Hisilicon hip05, + Hi1610 and further ICT SoC + +config HNS_ENET + tristate "Hisilicon HNS Ethernet Device Support" + select PHYLIB + select HNS + ---help--- + This selects the general ethernet driver for HNS. This module make + use of any HNS AE driver, such as HNS_DSAF + endif # NET_VENDOR_HISILICON diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile index 6c14540a4dc5..390b71fb3000 100644 --- a/drivers/net/ethernet/hisilicon/Makefile +++ b/drivers/net/ethernet/hisilicon/Makefile @@ -3,4 +3,6 @@ # obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o -obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o hip04_eth.o +obj-$(CONFIG_HIP04_ETH) += hip04_eth.o +obj-$(CONFIG_HNS_MDIO) += hns_mdio.o +obj-$(CONFIG_HNS) += hns/ diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c index cc2d8b4b18e3..253f8ed0537a 100644 --- a/drivers/net/ethernet/hisilicon/hip04_eth.c +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c @@ -816,7 +816,7 @@ static int hip04_mac_probe(struct platform_device *pdev) struct net_device *ndev; struct hip04_priv *priv; struct resource *res; - unsigned int irq; + int irq; int ret; ndev = alloc_etherdev(sizeof(struct hip04_priv)); diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c deleted file mode 100644 index fca0a5be1f0f..000000000000 --- a/drivers/net/ethernet/hisilicon/hip04_mdio.c +++ /dev/null @@ -1,185 +0,0 @@ -/* Copyright (c) 2014 Linaro Ltd. - * Copyright (c) 2014 Hisilicon Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/of_mdio.h> -#include <linux/delay.h> - -#define MDIO_CMD_REG 0x0 -#define MDIO_ADDR_REG 0x4 -#define MDIO_WDATA_REG 0x8 -#define MDIO_RDATA_REG 0xc -#define MDIO_STA_REG 0x10 - -#define MDIO_START BIT(14) -#define MDIO_R_VALID BIT(1) -#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START) -#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START) - -struct hip04_mdio_priv { - void __iomem *base; -}; - -#define WAIT_TIMEOUT 10 -static int hip04_mdio_wait_ready(struct mii_bus *bus) -{ - struct hip04_mdio_priv *priv = bus->priv; - int i; - - for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) { - if (i == WAIT_TIMEOUT) - return -ETIMEDOUT; - msleep(20); - } - - return 0; -} - -static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum) -{ - struct hip04_mdio_priv *priv = bus->priv; - u32 val; - int ret; - - ret = hip04_mdio_wait_ready(bus); - if (ret < 0) - goto out; - - val = regnum | (mii_id << 5) | MDIO_READ; - writel_relaxed(val, priv->base + MDIO_CMD_REG); - - ret = hip04_mdio_wait_ready(bus); - if (ret < 0) - goto out; - - val = readl_relaxed(priv->base + MDIO_STA_REG); - if (val & MDIO_R_VALID) { - dev_err(bus->parent, "SMI bus read not valid\n"); - ret = -ENODEV; - goto out; - } - - val = readl_relaxed(priv->base + MDIO_RDATA_REG); - ret = val & 0xFFFF; -out: - return ret; -} - -static int hip04_mdio_write(struct mii_bus *bus, int mii_id, - int regnum, u16 value) -{ - struct hip04_mdio_priv *priv = bus->priv; - u32 val; - int ret; - - ret = hip04_mdio_wait_ready(bus); - if (ret < 0) - goto out; - - writel_relaxed(value, priv->base + MDIO_WDATA_REG); - val = regnum | (mii_id << 5) | MDIO_WRITE; - writel_relaxed(val, priv->base + MDIO_CMD_REG); -out: - return ret; -} - -static int hip04_mdio_reset(struct mii_bus *bus) -{ - int temp, i; - - for (i = 0; i < PHY_MAX_ADDR; i++) { - hip04_mdio_write(bus, i, 22, 0); - temp = hip04_mdio_read(bus, i, MII_BMCR); - if (temp < 0) - continue; - - temp |= BMCR_RESET; - if (hip04_mdio_write(bus, i, MII_BMCR, temp) < 0) - continue; - } - - mdelay(500); - return 0; -} - -static int hip04_mdio_probe(struct platform_device *pdev) -{ - struct resource *r; - struct mii_bus *bus; - struct hip04_mdio_priv *priv; - int ret; - - bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv)); - if (!bus) { - dev_err(&pdev->dev, "Cannot allocate MDIO bus\n"); - return -ENOMEM; - } - - bus->name = "hip04_mdio_bus"; - bus->read = hip04_mdio_read; - bus->write = hip04_mdio_write; - bus->reset = hip04_mdio_reset; - snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev)); - bus->parent = &pdev->dev; - priv = bus->priv; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(priv->base)) { - ret = PTR_ERR(priv->base); - goto out_mdio; - } - - ret = of_mdiobus_register(bus, pdev->dev.of_node); - if (ret < 0) { - dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret); - goto out_mdio; - } - - platform_set_drvdata(pdev, bus); - - return 0; - -out_mdio: - mdiobus_free(bus); - return ret; -} - -static int hip04_mdio_remove(struct platform_device *pdev) -{ - struct mii_bus *bus = platform_get_drvdata(pdev); - - mdiobus_unregister(bus); - mdiobus_free(bus); - - return 0; -} - -static const struct of_device_id hip04_mdio_match[] = { - { .compatible = "hisilicon,hip04-mdio" }, - { } -}; -MODULE_DEVICE_TABLE(of, hip04_mdio_match); - -static struct platform_driver hip04_mdio_driver = { - .probe = hip04_mdio_probe, - .remove = hip04_mdio_remove, - .driver = { - .name = "hip04-mdio", - .of_match_table = hip04_mdio_match, - }, -}; - -module_platform_driver(hip04_mdio_driver); - -MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:hip04-mdio"); diff --git a/drivers/net/ethernet/hisilicon/hns/Makefile b/drivers/net/ethernet/hisilicon/hns/Makefile new file mode 100644 index 000000000000..6010c83e38d8 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the HISILICON network device drivers. +# + +obj-$(CONFIG_HNS) += hnae.o + +obj-$(CONFIG_HNS_DSAF) += hns_dsaf.o +hns_dsaf-objs = hns_ae_adapt.o hns_dsaf_gmac.o hns_dsaf_mac.o hns_dsaf_misc.o \ + hns_dsaf_main.o hns_dsaf_ppe.o hns_dsaf_rcb.o hns_dsaf_xgmac.o + +obj-$(CONFIG_HNS_ENET) += hns_enet_drv.o +hns_enet_drv-objs = hns_enet.o hns_ethtool.o diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c new file mode 100644 index 000000000000..f52e99acf463 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/skbuff.h> +#include <linux/slab.h> + +#include "hnae.h" + +#define cls_to_ae_dev(dev) container_of(dev, struct hnae_ae_dev, cls_dev) + +static struct class *hnae_class; + +static void +hnae_list_add(spinlock_t *lock, struct list_head *node, struct list_head *head) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + list_add_tail_rcu(node, head); + spin_unlock_irqrestore(lock, flags); +} + +static void hnae_list_del(spinlock_t *lock, struct list_head *node) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + list_del_rcu(node); + spin_unlock_irqrestore(lock, flags); +} + +static int hnae_alloc_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + unsigned int order = hnae_page_order(ring); + struct page *p = dev_alloc_pages(order); + + if (!p) + return -ENOMEM; + + cb->priv = p; + cb->page_offset = 0; + cb->reuse_flag = 0; + cb->buf = page_address(p); + cb->length = hnae_page_size(ring); + cb->type = DESC_TYPE_PAGE; + + return 0; +} + +static void hnae_free_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + if (cb->type == DESC_TYPE_SKB) + dev_kfree_skb_any((struct sk_buff *)cb->priv); + else if (unlikely(is_rx_ring(ring))) + put_page((struct page *)cb->priv); + memset(cb, 0, sizeof(*cb)); +} + +static int hnae_map_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + cb->dma = dma_map_page(ring_to_dev(ring), cb->priv, 0, + cb->length, ring_to_dma_dir(ring)); + + if (dma_mapping_error(ring_to_dev(ring), cb->dma)) + return -EIO; + + return 0; +} + +static void hnae_unmap_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + if (cb->type == DESC_TYPE_SKB) + dma_unmap_single(ring_to_dev(ring), cb->dma, cb->length, + ring_to_dma_dir(ring)); + else + dma_unmap_page(ring_to_dev(ring), cb->dma, cb->length, + ring_to_dma_dir(ring)); +} + +static struct hnae_buf_ops hnae_bops = { + .alloc_buffer = hnae_alloc_buffer, + .free_buffer = hnae_free_buffer, + .map_buffer = hnae_map_buffer, + .unmap_buffer = hnae_unmap_buffer, +}; + +static int __ae_match(struct device *dev, const void *data) +{ + struct hnae_ae_dev *hdev = cls_to_ae_dev(dev); + const char *ae_id = data; + + if (!strncmp(ae_id, hdev->name, AE_NAME_SIZE)) + return 1; + + return 0; +} + +static struct hnae_ae_dev *find_ae(const char *ae_id) +{ + struct device *dev; + + WARN_ON(!ae_id); + + dev = class_find_device(hnae_class, NULL, ae_id, __ae_match); + + return dev ? cls_to_ae_dev(dev) : NULL; +} + +static void hnae_free_buffers(struct hnae_ring *ring) +{ + int i; + + for (i = 0; i < ring->desc_num; i++) + hnae_free_buffer_detach(ring, i); +} + +/* Allocate memory for raw pkg, and map with dma */ +static int hnae_alloc_buffers(struct hnae_ring *ring) +{ + int i, j, ret; + + for (i = 0; i < ring->desc_num; i++) { + ret = hnae_alloc_buffer_attach(ring, i); + if (ret) + goto out_buffer_fail; + } + + return 0; + +out_buffer_fail: + for (j = i - 1; j >= 0; j--) + hnae_free_buffer_detach(ring, j); + return ret; +} + +/* free desc along with its attached buffer */ +static void hnae_free_desc(struct hnae_ring *ring) +{ + hnae_free_buffers(ring); + dma_unmap_single(ring_to_dev(ring), ring->desc_dma_addr, + ring->desc_num * sizeof(ring->desc[0]), + ring_to_dma_dir(ring)); + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; +} + +/* alloc desc, without buffer attached */ +static int hnae_alloc_desc(struct hnae_ring *ring) +{ + int size = ring->desc_num * sizeof(ring->desc[0]); + + ring->desc = kzalloc(size, GFP_KERNEL); + if (!ring->desc) + return -ENOMEM; + + ring->desc_dma_addr = dma_map_single(ring_to_dev(ring), + ring->desc, size, ring_to_dma_dir(ring)); + if (dma_mapping_error(ring_to_dev(ring), ring->desc_dma_addr)) { + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; + return -ENOMEM; + } + + return 0; +} + +/* fini ring, also free the buffer for the ring */ +static void hnae_fini_ring(struct hnae_ring *ring) +{ + hnae_free_desc(ring); + kfree(ring->desc_cb); + ring->desc_cb = NULL; + ring->next_to_clean = 0; + ring->next_to_use = 0; +} + +/* init ring, and with buffer for rx ring */ +static int +hnae_init_ring(struct hnae_queue *q, struct hnae_ring *ring, int flags) +{ + int ret; + + if (ring->desc_num <= 0 || ring->buf_size <= 0) + return -EINVAL; + + ring->q = q; + ring->flags = flags; + assert(!ring->desc && !ring->desc_cb && !ring->desc_dma_addr); + + /* not matter for tx or rx ring, the ntc and ntc start from 0 */ + assert(ring->next_to_use == 0); + assert(ring->next_to_clean == 0); + + ring->desc_cb = kcalloc(ring->desc_num, sizeof(ring->desc_cb[0]), + GFP_KERNEL); + if (!ring->desc_cb) { + ret = -ENOMEM; + goto out; + } + + ret = hnae_alloc_desc(ring); + if (ret) + goto out_with_desc_cb; + + if (is_rx_ring(ring)) { + ret = hnae_alloc_buffers(ring); + if (ret) + goto out_with_desc; + } + + return 0; + +out_with_desc: + hnae_free_desc(ring); +out_with_desc_cb: + kfree(ring->desc_cb); + ring->desc_cb = NULL; +out: + return ret; +} + +static int hnae_init_queue(struct hnae_handle *h, struct hnae_queue *q, + struct hnae_ae_dev *dev) +{ + int ret; + + q->dev = dev; + q->handle = h; + + ret = hnae_init_ring(q, &q->tx_ring, q->tx_ring.flags | RINGF_DIR); + if (ret) + goto out; + + ret = hnae_init_ring(q, &q->rx_ring, q->rx_ring.flags & ~RINGF_DIR); + if (ret) + goto out_with_tx_ring; + + if (dev->ops->init_queue) + dev->ops->init_queue(q); + + return 0; + +out_with_tx_ring: + hnae_fini_ring(&q->tx_ring); +out: + return ret; +} + +static void hnae_fini_queue(struct hnae_queue *q) +{ + if (q->dev->ops->fini_queue) + q->dev->ops->fini_queue(q); + + hnae_fini_ring(&q->tx_ring); + hnae_fini_ring(&q->rx_ring); +} + +/** + * ae_chain - define ae chain head + */ +static RAW_NOTIFIER_HEAD(ae_chain); + +int hnae_register_notifier(struct notifier_block *nb) +{ + return raw_notifier_chain_register(&ae_chain, nb); +} +EXPORT_SYMBOL(hnae_register_notifier); + +void hnae_unregister_notifier(struct notifier_block *nb) +{ + if (raw_notifier_chain_unregister(&ae_chain, nb)) + dev_err(NULL, "notifier chain unregister fail\n"); +} +EXPORT_SYMBOL(hnae_unregister_notifier); + +int hnae_reinit_handle(struct hnae_handle *handle) +{ + int i, j; + int ret; + + for (i = 0; i < handle->q_num; i++) /* free ring*/ + hnae_fini_queue(handle->qs[i]); + + if (handle->dev->ops->reset) + handle->dev->ops->reset(handle); + + for (i = 0; i < handle->q_num; i++) {/* reinit ring*/ + ret = hnae_init_queue(handle, handle->qs[i], handle->dev); + if (ret) + goto out_when_init_queue; + } + return 0; +out_when_init_queue: + for (j = i - 1; j >= 0; j--) + hnae_fini_queue(handle->qs[j]); + return ret; +} +EXPORT_SYMBOL(hnae_reinit_handle); + +/* hnae_get_handle - get a handle from the AE + * @owner_dev: the dev use this handle + * @ae_id: the id of the ae to be used + * @ae_opts: the options set for the handle + * @bops: the callbacks for buffer management + * + * return handle ptr or ERR_PTR + */ +struct hnae_handle *hnae_get_handle(struct device *owner_dev, + const char *ae_id, u32 port_id, + struct hnae_buf_ops *bops) +{ + struct hnae_ae_dev *dev; + struct hnae_handle *handle; + int i, j; + int ret; + + dev = find_ae(ae_id); + if (!dev) + return ERR_PTR(-ENODEV); + + handle = dev->ops->get_handle(dev, port_id); + if (IS_ERR(handle)) + return handle; + + handle->dev = dev; + handle->owner_dev = owner_dev; + handle->bops = bops ? bops : &hnae_bops; + handle->eport_id = port_id; + + for (i = 0; i < handle->q_num; i++) { + ret = hnae_init_queue(handle, handle->qs[i], dev); + if (ret) + goto out_when_init_queue; + } + + __module_get(dev->owner); + + hnae_list_add(&dev->lock, &handle->node, &dev->handle_list); + + return handle; + +out_when_init_queue: + for (j = i - 1; j >= 0; j--) + hnae_fini_queue(handle->qs[j]); + + return ERR_PTR(-ENOMEM); +} +EXPORT_SYMBOL(hnae_get_handle); + +void hnae_put_handle(struct hnae_handle *h) +{ + struct hnae_ae_dev *dev = h->dev; + int i; + + for (i = 0; i < h->q_num; i++) + hnae_fini_queue(h->qs[i]); + + if (h->dev->ops->reset) + h->dev->ops->reset(h); + + hnae_list_del(&dev->lock, &h->node); + + if (dev->ops->put_handle) + dev->ops->put_handle(h); + + module_put(dev->owner); +} +EXPORT_SYMBOL(hnae_put_handle); + +static void hnae_release(struct device *dev) +{ +} + +/** + * hnae_ae_register - register a AE engine to hnae framework + * @hdev: the hnae ae engine device + * @owner: the module who provides this dev + * NOTE: the duplicated name will not be checked + */ +int hnae_ae_register(struct hnae_ae_dev *hdev, struct module *owner) +{ + static atomic_t id = ATOMIC_INIT(-1); + int ret; + + if (!hdev->dev) + return -ENODEV; + + if (!hdev->ops || !hdev->ops->get_handle || + !hdev->ops->toggle_ring_irq || + !hdev->ops->toggle_queue_status || + !hdev->ops->get_status || !hdev->ops->adjust_link) + return -EINVAL; + + hdev->owner = owner; + hdev->id = (int)atomic_inc_return(&id); + hdev->cls_dev.parent = hdev->dev; + hdev->cls_dev.class = hnae_class; + hdev->cls_dev.release = hnae_release; + (void)dev_set_name(&hdev->cls_dev, "hnae%d", hdev->id); + ret = device_register(&hdev->cls_dev); + if (ret) + return ret; + + __module_get(THIS_MODULE); + + INIT_LIST_HEAD(&hdev->handle_list); + spin_lock_init(&hdev->lock); + + ret = raw_notifier_call_chain(&ae_chain, HNAE_AE_REGISTER, NULL); + if (ret) + dev_dbg(hdev->dev, + "has not notifier for AE: %s\n", hdev->name); + + return 0; +} +EXPORT_SYMBOL(hnae_ae_register); + +/** + * hnae_ae_unregister - unregisters a HNAE AE engine + * @cdev: the device to unregister + */ +void hnae_ae_unregister(struct hnae_ae_dev *hdev) +{ + device_unregister(&hdev->cls_dev); + module_put(THIS_MODULE); +} +EXPORT_SYMBOL(hnae_ae_unregister); + +static ssize_t handles_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t s = 0; + struct hnae_ae_dev *hdev = cls_to_ae_dev(dev); + struct hnae_handle *h; + int i = 0, j; + + list_for_each_entry_rcu(h, &hdev->handle_list, node) { + s += sprintf(buf + s, "handle %d (eport_id=%u from %s):\n", + i++, h->eport_id, h->dev->name); + for (j = 0; j < h->q_num; j++) { + s += sprintf(buf + s, "\tqueue[%d] on %p\n", + j, h->qs[i]->io_base); +#define HANDEL_TX_MSG "\t\ttx_ring on %p:%u,%u,%u,%u,%u,%llu,%llu\n" + s += sprintf(buf + s, + HANDEL_TX_MSG, + h->qs[i]->tx_ring.io_base, + h->qs[i]->tx_ring.buf_size, + h->qs[i]->tx_ring.desc_num, + h->qs[i]->tx_ring.max_desc_num_per_pkt, + h->qs[i]->tx_ring.max_raw_data_sz_per_desc, + h->qs[i]->tx_ring.max_pkt_size, + h->qs[i]->tx_ring.stats.sw_err_cnt, + h->qs[i]->tx_ring.stats.io_err_cnt); + s += sprintf(buf + s, + "\t\trx_ring on %p:%u,%u,%llu,%llu,%llu\n", + h->qs[i]->rx_ring.io_base, + h->qs[i]->rx_ring.buf_size, + h->qs[i]->rx_ring.desc_num, + h->qs[i]->rx_ring.stats.sw_err_cnt, + h->qs[i]->rx_ring.stats.io_err_cnt, + h->qs[i]->rx_ring.stats.seg_pkt_cnt); + } + } + + return s; +} + +static DEVICE_ATTR_RO(handles); +static struct attribute *hnae_class_attrs[] = { + &dev_attr_handles.attr, + NULL, +}; +ATTRIBUTE_GROUPS(hnae_class); + +static int __init hnae_init(void) +{ + hnae_class = class_create(THIS_MODULE, "hnae"); + if (IS_ERR(hnae_class)) + return PTR_ERR(hnae_class); + + hnae_class->dev_groups = hnae_class_groups; + return 0; +} + +static void __exit hnae_exit(void) +{ + class_destroy(hnae_class); +} + +subsys_initcall(hnae_init); +module_exit(hnae_exit); + +MODULE_AUTHOR("Hisilicon, Inc."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Hisilicon Network Acceleration Engine Framework"); + +/* vi: set tw=78 noet: */ diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h new file mode 100644 index 000000000000..d4a1eb1b8e54 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -0,0 +1,584 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HNAE_H +#define __HNAE_H + +/* Names used in this framework: + * ae handle (handle): + * a set of queues provided by AE + * ring buffer queue (rbq): + * the channel between upper layer and the AE, can do tx and rx + * ring: + * a tx or rx channel within a rbq + * ring description (desc): + * an element in the ring with packet information + * buffer: + * a memory region referred by desc with the full packet payload + * + * "num" means a static number set as a parameter, "count" mean a dynamic + * number set while running + * "cb" means control block + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/notifier.h> +#include <linux/phy.h> +#include <linux/types.h> + +#define HNAE_DRIVER_VERSION "1.3.0" +#define HNAE_DRIVER_NAME "hns" +#define HNAE_COPYRIGHT "Copyright(c) 2015 Huawei Corporation." +#define HNAE_DRIVER_STRING "Hisilicon Network Subsystem Driver" +#define HNAE_DEFAULT_DEVICE_DESCR "Hisilicon Network Subsystem" + +#ifdef DEBUG + +#ifndef assert +#define assert(expr) \ +do { \ + if (!(expr)) { \ + pr_err("Assertion failed! %s, %s, %s, line %d\n", \ + #expr, __FILE__, __func__, __LINE__); \ + } \ +} while (0) +#endif + +#else + +#ifndef assert +#define assert(expr) +#endif + +#endif + +#define AE_VERSION_1 ('6' << 16 | '6' << 8 | '0') +#define AE_VERSION_2 ('1' << 24 | '6' << 16 | '1' << 8 | '0') +#define AE_NAME_SIZE 16 + +/* some said the RX and TX RCB format should not be the same in the future. But + * it is the same now... + */ +#define RCB_REG_BASEADDR_L 0x00 /* P660 support only 32bit accessing */ +#define RCB_REG_BASEADDR_H 0x04 +#define RCB_REG_BD_NUM 0x08 +#define RCB_REG_BD_LEN 0x0C +#define RCB_REG_PKTLINE 0x10 +#define RCB_REG_TAIL 0x18 +#define RCB_REG_HEAD 0x1C +#define RCB_REG_FBDNUM 0x20 +#define RCB_REG_OFFSET 0x24 /* pkt num to be handled */ +#define RCB_REG_PKTNUM_RECORD 0x2C /* total pkt received */ + +#define HNS_RX_HEAD_SIZE 256 + +#define HNAE_AE_REGISTER 0x1 + +#define RCB_RING_NAME_LEN 16 + +enum hnae_led_state { + HNAE_LED_INACTIVE, + HNAE_LED_ACTIVE, + HNAE_LED_ON, + HNAE_LED_OFF +}; + +#define HNS_RX_FLAG_VLAN_PRESENT 0x1 +#define HNS_RX_FLAG_L3ID_IPV4 0x0 +#define HNS_RX_FLAG_L3ID_IPV6 0x1 +#define HNS_RX_FLAG_L4ID_UDP 0x0 +#define HNS_RX_FLAG_L4ID_TCP 0x1 + +#define HNS_TXD_ASID_S 0 +#define HNS_TXD_ASID_M (0xff << HNS_TXD_ASID_S) +#define HNS_TXD_BUFNUM_S 8 +#define HNS_TXD_BUFNUM_M (0x3 << HNS_TXD_BUFNUM_S) +#define HNS_TXD_PORTID_S 10 +#define HNS_TXD_PORTID_M (0x7 << HNS_TXD_PORTID_S) + +#define HNS_TXD_RA_B 8 +#define HNS_TXD_RI_B 9 +#define HNS_TXD_L4CS_B 10 +#define HNS_TXD_L3CS_B 11 +#define HNS_TXD_FE_B 12 +#define HNS_TXD_VLD_B 13 +#define HNS_TXD_IPOFFSET_S 14 +#define HNS_TXD_IPOFFSET_M (0xff << HNS_TXD_IPOFFSET_S) + +#define HNS_RXD_IPOFFSET_S 0 +#define HNS_RXD_IPOFFSET_M (0xff << HNS_TXD_IPOFFSET_S) +#define HNS_RXD_BUFNUM_S 8 +#define HNS_RXD_BUFNUM_M (0x3 << HNS_RXD_BUFNUM_S) +#define HNS_RXD_PORTID_S 10 +#define HNS_RXD_PORTID_M (0x7 << HNS_RXD_PORTID_S) +#define HNS_RXD_DMAC_S 13 +#define HNS_RXD_DMAC_M (0x3 << HNS_RXD_DMAC_S) +#define HNS_RXD_VLAN_S 15 +#define HNS_RXD_VLAN_M (0x3 << HNS_RXD_VLAN_S) +#define HNS_RXD_L3ID_S 17 +#define HNS_RXD_L3ID_M (0xf << HNS_RXD_L3ID_S) +#define HNS_RXD_L4ID_S 21 +#define HNS_RXD_L4ID_M (0xf << HNS_RXD_L4ID_S) +#define HNS_RXD_FE_B 25 +#define HNS_RXD_FRAG_B 26 +#define HNS_RXD_VLD_B 27 +#define HNS_RXD_L2E_B 28 +#define HNS_RXD_L3E_B 29 +#define HNS_RXD_L4E_B 30 +#define HNS_RXD_DROP_B 31 + +#define HNS_RXD_VLANID_S 8 +#define HNS_RXD_VLANID_M (0xfff << HNS_RXD_VLANID_S) +#define HNS_RXD_CFI_B 20 +#define HNS_RXD_PRI_S 21 +#define HNS_RXD_PRI_M (0x7 << HNS_RXD_PRI_S) +#define HNS_RXD_ASID_S 24 +#define HNS_RXD_ASID_M (0xff << HNS_RXD_ASID_S) + +/* hardware spec ring buffer format */ +struct __packed hnae_desc { + __le64 addr; + union { + struct { + __le16 asid_bufnum_pid; + __le16 send_size; + __le32 flag_ipoffset; + __le32 reserved_3[4]; + } tx; + + struct { + __le32 ipoff_bnum_pid_flag; + __le16 pkt_len; + __le16 size; + __le32 vlan_pri_asid; + __le32 reserved_2[3]; + } rx; + }; +}; + +struct hnae_desc_cb { + dma_addr_t dma; /* dma address of this desc */ + void *buf; /* cpu addr for a desc */ + + /* priv data for the desc, e.g. skb when use with ip stack*/ + void *priv; + u16 page_offset; + u16 reuse_flag; + + u16 length; /* length of the buffer */ + + /* desc type, used by the ring user to mark the type of the priv data */ + u16 type; +}; + +#define setflags(flags, bits) ((flags) |= (bits)) +#define unsetflags(flags, bits) ((flags) &= ~(bits)) + +/* hnae_ring->flags fields */ +#define RINGF_DIR 0x1 /* TX or RX ring, set if TX */ +#define is_tx_ring(ring) ((ring)->flags & RINGF_DIR) +#define is_rx_ring(ring) (!is_tx_ring(ring)) +#define ring_to_dma_dir(ring) (is_tx_ring(ring) ? \ + DMA_TO_DEVICE : DMA_FROM_DEVICE) + +struct ring_stats { + u64 io_err_cnt; + u64 sw_err_cnt; + u64 seg_pkt_cnt; + union { + struct { + u64 tx_pkts; + u64 tx_bytes; + u64 tx_err_cnt; + u64 restart_queue; + u64 tx_busy; + }; + struct { + u64 rx_pkts; + u64 rx_bytes; + u64 rx_err_cnt; + u64 reuse_pg_cnt; + u64 err_pkt_len; + u64 non_vld_descs; + u64 err_bd_num; + u64 l2_err; + u64 l3l4_csum_err; + }; + }; +}; + +struct hnae_queue; + +struct hnae_ring { + u8 __iomem *io_base; /* base io address for the ring */ + struct hnae_desc *desc; /* dma map address space */ + struct hnae_desc_cb *desc_cb; + struct hnae_queue *q; + int irq; + char ring_name[RCB_RING_NAME_LEN]; + + /* statistic */ + struct ring_stats stats; + + dma_addr_t desc_dma_addr; + u32 buf_size; /* size for hnae_desc->addr, preset by AE */ + u16 desc_num; /* total number of desc */ + u16 max_desc_num_per_pkt; + u16 max_raw_data_sz_per_desc; + u16 max_pkt_size; + int next_to_use; /* idx of next spare desc */ + + /* idx of lastest sent desc, the ring is empty when equal to + * next_to_use + */ + int next_to_clean; + + int flags; /* ring attribute */ + int irq_init_flag; +}; + +#define ring_ptr_move_fw(ring, p) \ + ((ring)->p = ((ring)->p + 1) % (ring)->desc_num) +#define ring_ptr_move_bw(ring, p) \ + ((ring)->p = ((ring)->p - 1 + (ring)->desc_num) % (ring)->desc_num) + +enum hns_desc_type { + DESC_TYPE_SKB, + DESC_TYPE_PAGE, +}; + +#define assert_is_ring_idx(ring, idx) \ + assert((idx) >= 0 && (idx) < (ring)->desc_num) + +/* the distance between [begin, end) in a ring buffer + * note: there is a unuse slot between the begin and the end + */ +static inline int ring_dist(struct hnae_ring *ring, int begin, int end) +{ + assert_is_ring_idx(ring, begin); + assert_is_ring_idx(ring, end); + + return (end - begin + ring->desc_num) % ring->desc_num; +} + +static inline int ring_space(struct hnae_ring *ring) +{ + return ring->desc_num - + ring_dist(ring, ring->next_to_clean, ring->next_to_use) - 1; +} + +static inline int is_ring_empty(struct hnae_ring *ring) +{ + assert_is_ring_idx(ring, ring->next_to_use); + assert_is_ring_idx(ring, ring->next_to_clean); + + return ring->next_to_use == ring->next_to_clean; +} + +#define hnae_buf_size(_ring) ((_ring)->buf_size) +#define hnae_page_order(_ring) (get_order(hnae_buf_size(_ring))) +#define hnae_page_size(_ring) (PAGE_SIZE << hnae_page_order(_ring)) + +struct hnae_handle; + +/* allocate and dma map space for hnae desc */ +struct hnae_buf_ops { + int (*alloc_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); + void (*free_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); + int (*map_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); + void (*unmap_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); +}; + +struct hnae_queue { + void __iomem *io_base; + phys_addr_t phy_base; + struct hnae_ae_dev *dev; /* the device who use this queue */ + struct hnae_ring rx_ring, tx_ring; + struct hnae_handle *handle; +}; + +/*hnae loop mode*/ +enum hnae_loop { + MAC_INTERNALLOOP_MAC = 0, + MAC_INTERNALLOOP_SERDES, + MAC_INTERNALLOOP_PHY, + MAC_LOOP_NONE, +}; + +/*hnae port type*/ +enum hnae_port_type { + HNAE_PORT_SERVICE = 0, + HNAE_PORT_DEBUG +}; + +/* This struct defines the operation on the handle. + * + * get_handle(): (mandatory) + * Get a handle from AE according to its name and options. + * the AE driver should manage the space used by handle and its queues while + * the HNAE framework will allocate desc and desc_cb for all rings in the + * queues. + * put_handle(): + * Release the handle. + * start(): + * Enable the hardware, include all queues + * stop(): + * Disable the hardware + * set_opts(): (mandatory) + * Set options to the AE + * get_opts(): (mandatory) + * Get options from the AE + * get_status(): + * Get the carrier state of the back channel of the handle, 1 for ok, 0 for + * non-ok + * toggle_ring_irq(): (mandatory) + * Set the ring irq to be enabled(0) or disable(1) + * toggle_queue_status(): (mandatory) + * Set the queue to be enabled(1) or disable(0), this will not change the + * ring irq state + * adjust_link() + * adjust link status + * set_loopback() + * set loopback + * get_ring_bdnum_limit() + * get ring bd number limit + * get_pauseparam() + * get tx and rx of pause frame use + * set_autoneg() + * set auto autonegotiation of pause frame use + * get_autoneg() + * get auto autonegotiation of pause frame use + * set_pauseparam() + * set tx and rx of pause frame use + * get_coalesce_usecs() + * get usecs to delay a TX interrupt after a packet is sent + * get_rx_max_coalesced_frames() + * get Maximum number of packets to be sent before a TX interrupt. + * set_coalesce_usecs() + * set usecs to delay a TX interrupt after a packet is sent + * set_coalesce_frames() + * set Maximum number of packets to be sent before a TX interrupt. + * get_ringnum() + * get RX/TX ring number + * get_max_ringnum() + * get RX/TX ring maximum number + * get_mac_addr() + * get mac address + * set_mac_addr() + * set mac address + * set_mc_addr() + * set multicast mode + * set_mtu() + * set mtu + * update_stats() + * update Old network device statistics + * get_ethtool_stats() + * get ethtool network device statistics + * get_strings() + * get a set of strings that describe the requested objects + * get_sset_count() + * get number of strings that @get_strings will write + * update_led_status() + * update the led status + * set_led_id() + * set led id + * get_regs() + * get regs dump + * get_regs_len() + * get the len of the regs dump + */ +struct hnae_ae_ops { + struct hnae_handle *(*get_handle)(struct hnae_ae_dev *dev, + u32 port_id); + void (*put_handle)(struct hnae_handle *handle); + void (*init_queue)(struct hnae_queue *q); + void (*fini_queue)(struct hnae_queue *q); + int (*start)(struct hnae_handle *handle); + void (*stop)(struct hnae_handle *handle); + void (*reset)(struct hnae_handle *handle); + int (*set_opts)(struct hnae_handle *handle, int type, void *opts); + int (*get_opts)(struct hnae_handle *handle, int type, void **opts); + int (*get_status)(struct hnae_handle *handle); + int (*get_info)(struct hnae_handle *handle, + u8 *auto_neg, u16 *speed, u8 *duplex); + void (*toggle_ring_irq)(struct hnae_ring *ring, u32 val); + void (*toggle_queue_status)(struct hnae_queue *queue, u32 val); + void (*adjust_link)(struct hnae_handle *handle, int speed, int duplex); + int (*set_loopback)(struct hnae_handle *handle, + enum hnae_loop loop_mode, int en); + void (*get_ring_bdnum_limit)(struct hnae_queue *queue, + u32 *uplimit); + void (*get_pauseparam)(struct hnae_handle *handle, + u32 *auto_neg, u32 *rx_en, u32 *tx_en); + int (*set_autoneg)(struct hnae_handle *handle, u8 enable); + int (*get_autoneg)(struct hnae_handle *handle); + int (*set_pauseparam)(struct hnae_handle *handle, + u32 auto_neg, u32 rx_en, u32 tx_en); + void (*get_coalesce_usecs)(struct hnae_handle *handle, + u32 *tx_usecs, u32 *rx_usecs); + void (*get_rx_max_coalesced_frames)(struct hnae_handle *handle, + u32 *tx_frames, u32 *rx_frames); + void (*set_coalesce_usecs)(struct hnae_handle *handle, u32 timeout); + int (*set_coalesce_frames)(struct hnae_handle *handle, + u32 coalesce_frames); + int (*get_mac_addr)(struct hnae_handle *handle, void **p); + int (*set_mac_addr)(struct hnae_handle *handle, void *p); + int (*set_mc_addr)(struct hnae_handle *handle, void *addr); + int (*set_mtu)(struct hnae_handle *handle, int new_mtu); + void (*update_stats)(struct hnae_handle *handle, + struct net_device_stats *net_stats); + void (*get_stats)(struct hnae_handle *handle, u64 *data); + void (*get_strings)(struct hnae_handle *handle, + u32 stringset, u8 *data); + int (*get_sset_count)(struct hnae_handle *handle, int stringset); + void (*update_led_status)(struct hnae_handle *handle); + int (*set_led_id)(struct hnae_handle *handle, + enum hnae_led_state status); + void (*get_regs)(struct hnae_handle *handle, void *data); + int (*get_regs_len)(struct hnae_handle *handle); +}; + +struct hnae_ae_dev { + struct device cls_dev; /* the class dev */ + struct device *dev; /* the presented dev */ + struct hnae_ae_ops *ops; + struct list_head node; + struct module *owner; /* the module who provides this dev */ + int id; + char name[AE_NAME_SIZE]; + struct list_head handle_list; + spinlock_t lock; /* lock to protect the handle_list */ +}; + +struct hnae_handle { + struct device *owner_dev; /* the device which make use of this handle */ + struct hnae_ae_dev *dev; /* the device who provides this handle */ + struct device_node *phy_node; + phy_interface_t phy_if; + u32 if_support; + int q_num; + int vf_id; + u32 eport_id; + enum hnae_port_type port_type; + struct list_head node; /* list to hnae_ae_dev->handle_list */ + struct hnae_buf_ops *bops; /* operation for the buffer */ + struct hnae_queue **qs; /* array base of all queues */ +}; + +#define ring_to_dev(ring) ((ring)->q->dev->dev) + +struct hnae_handle *hnae_get_handle(struct device *owner_dev, const char *ae_id, + u32 port_id, struct hnae_buf_ops *bops); +void hnae_put_handle(struct hnae_handle *handle); +int hnae_ae_register(struct hnae_ae_dev *dev, struct module *owner); +void hnae_ae_unregister(struct hnae_ae_dev *dev); + +int hnae_register_notifier(struct notifier_block *nb); +void hnae_unregister_notifier(struct notifier_block *nb); +int hnae_reinit_handle(struct hnae_handle *handle); + +#define hnae_queue_xmit(q, buf_num) writel_relaxed(buf_num, \ + (q)->tx_ring.io_base + RCB_REG_TAIL) + +#ifndef assert +#define assert(cond) +#endif + +static inline int hnae_reserve_buffer_map(struct hnae_ring *ring, + struct hnae_desc_cb *cb) +{ + struct hnae_buf_ops *bops = ring->q->handle->bops; + int ret; + + ret = bops->alloc_buffer(ring, cb); + if (ret) + goto out; + + ret = bops->map_buffer(ring, cb); + if (ret) + goto out_with_buf; + + return 0; + +out_with_buf: + bops->free_buffer(ring, cb); +out: + return ret; +} + +static inline int hnae_alloc_buffer_attach(struct hnae_ring *ring, int i) +{ + int ret = hnae_reserve_buffer_map(ring, &ring->desc_cb[i]); + + if (ret) + return ret; + + ring->desc[i].addr = (__le64)ring->desc_cb[i].dma; + + return 0; +} + +static inline void hnae_buffer_detach(struct hnae_ring *ring, int i) +{ + ring->q->handle->bops->unmap_buffer(ring, &ring->desc_cb[i]); + ring->desc[i].addr = 0; +} + +static inline void hnae_free_buffer_detach(struct hnae_ring *ring, int i) +{ + struct hnae_buf_ops *bops = ring->q->handle->bops; + struct hnae_desc_cb *cb = &ring->desc_cb[i]; + + if (!ring->desc_cb[i].dma) + return; + + hnae_buffer_detach(ring, i); + bops->free_buffer(ring, cb); +} + +/* detach a in-used buffer and replace with a reserved one */ +static inline void hnae_replace_buffer(struct hnae_ring *ring, int i, + struct hnae_desc_cb *res_cb) +{ + struct hnae_buf_ops *bops = ring->q->handle->bops; + struct hnae_desc_cb tmp_cb = ring->desc_cb[i]; + + bops->unmap_buffer(ring, &ring->desc_cb[i]); + ring->desc_cb[i] = *res_cb; + *res_cb = tmp_cb; + ring->desc[i].addr = (__le64)ring->desc_cb[i].dma; + ring->desc[i].rx.ipoff_bnum_pid_flag = 0; +} + +static inline void hnae_reuse_buffer(struct hnae_ring *ring, int i) +{ + ring->desc_cb[i].reuse_flag = 0; + ring->desc[i].addr = (__le64)(ring->desc_cb[i].dma + + ring->desc_cb[i].page_offset); + ring->desc[i].rx.ipoff_bnum_pid_flag = 0; +} + +#define hnae_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~(mask)); \ + (origin) |= ((val) << (shift)) & (mask); \ + } while (0) + +#define hnae_set_bit(origin, shift, val) \ + hnae_set_field((origin), (0x1 << (shift)), (shift), (val)) + +#define hnae_get_field(origin, mask, shift) (((origin) & (mask)) >> (shift)) + +#define hnae_get_bit(origin, shift) \ + hnae_get_field((origin), (0x1 << (shift)), (shift)) + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c new file mode 100644 index 000000000000..a2c72f84e397 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -0,0 +1,777 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/etherdevice.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> + +#include "hnae.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_main.h" +#include "hns_dsaf_ppe.h" +#include "hns_dsaf_rcb.h" + +#define AE_NAME_PORT_ID_IDX 6 +#define ETH_STATIC_REG 1 +#define ETH_DUMP_REG 5 +#define ETH_GSTRING_LEN 32 + +static struct hns_mac_cb *hns_get_mac_cb(struct hnae_handle *handle) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + return vf_cb->mac_cb; +} + +/** + * hns_ae_map_eport_to_dport - translate enet port id to dsaf port id + * @port_id: enet port id + *: debug port 0-1, service port 2 -7 (dsaf mode only 2) + * return: dsaf port id + *: service ports 0 - 5, debug port 6-7 + **/ +static int hns_ae_map_eport_to_dport(u32 port_id) +{ + int port_index; + + if (port_id < DSAF_DEBUG_NW_NUM) + port_index = port_id + DSAF_SERVICE_PORT_NUM_PER_DSAF; + else + port_index = port_id - DSAF_DEBUG_NW_NUM; + + return port_index; +} + +static struct dsaf_device *hns_ae_get_dsaf_dev(struct hnae_ae_dev *dev) +{ + return container_of(dev, struct dsaf_device, ae_dev); +} + +static struct hns_ppe_cb *hns_get_ppe_cb(struct hnae_handle *handle) +{ + int ppe_index; + int ppe_common_index; + struct ppe_common_cb *ppe_comm; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + if (vf_cb->port_index < DSAF_SERVICE_PORT_NUM_PER_DSAF) { + ppe_index = vf_cb->port_index; + ppe_common_index = 0; + } else { + ppe_index = 0; + ppe_common_index = + vf_cb->port_index - DSAF_SERVICE_PORT_NUM_PER_DSAF + 1; + } + ppe_comm = vf_cb->dsaf_dev->ppe_common[ppe_common_index]; + return &ppe_comm->ppe_cb[ppe_index]; +} + +static int hns_ae_get_q_num_per_vf( + struct dsaf_device *dsaf_dev, int port) +{ + int common_idx = hns_dsaf_get_comm_idx_by_port(port); + + return dsaf_dev->rcb_common[common_idx]->max_q_per_vf; +} + +static int hns_ae_get_vf_num_per_port( + struct dsaf_device *dsaf_dev, int port) +{ + int common_idx = hns_dsaf_get_comm_idx_by_port(port); + + return dsaf_dev->rcb_common[common_idx]->max_vfn; +} + +static struct ring_pair_cb *hns_ae_get_base_ring_pair( + struct dsaf_device *dsaf_dev, int port) +{ + int common_idx = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[common_idx]; + int q_num = rcb_comm->max_q_per_vf; + int vf_num = rcb_comm->max_vfn; + + if (common_idx == HNS_DSAF_COMM_SERVICE_NW_IDX) + return &rcb_comm->ring_pair_cb[port * q_num * vf_num]; + else + return &rcb_comm->ring_pair_cb[0]; +} + +static struct ring_pair_cb *hns_ae_get_ring_pair(struct hnae_queue *q) +{ + return container_of(q, struct ring_pair_cb, q); +} + +struct hnae_handle *hns_ae_get_handle(struct hnae_ae_dev *dev, + u32 port_id) +{ + int port_idx; + int vfnum_per_port; + int qnum_per_vf; + int i; + struct dsaf_device *dsaf_dev; + struct hnae_handle *ae_handle; + struct ring_pair_cb *ring_pair_cb; + struct hnae_vf_cb *vf_cb; + + dsaf_dev = hns_ae_get_dsaf_dev(dev); + port_idx = hns_ae_map_eport_to_dport(port_id); + + ring_pair_cb = hns_ae_get_base_ring_pair(dsaf_dev, port_idx); + vfnum_per_port = hns_ae_get_vf_num_per_port(dsaf_dev, port_idx); + qnum_per_vf = hns_ae_get_q_num_per_vf(dsaf_dev, port_idx); + + vf_cb = kzalloc(sizeof(*vf_cb) + + qnum_per_vf * sizeof(struct hnae_queue *), GFP_KERNEL); + if (unlikely(!vf_cb)) { + dev_err(dsaf_dev->dev, "malloc vf_cb fail!\n"); + ae_handle = ERR_PTR(-ENOMEM); + goto handle_err; + } + ae_handle = &vf_cb->ae_handle; + /* ae_handle Init */ + ae_handle->owner_dev = dsaf_dev->dev; + ae_handle->dev = dev; + ae_handle->q_num = qnum_per_vf; + + /* find ring pair, and set vf id*/ + for (ae_handle->vf_id = 0; + ae_handle->vf_id < vfnum_per_port; ae_handle->vf_id++) { + if (!ring_pair_cb->used_by_vf) + break; + ring_pair_cb += qnum_per_vf; + } + if (ae_handle->vf_id >= vfnum_per_port) { + dev_err(dsaf_dev->dev, "malloc queue fail!\n"); + ae_handle = ERR_PTR(-EINVAL); + goto vf_id_err; + } + + ae_handle->qs = (struct hnae_queue **)(&ae_handle->qs + 1); + for (i = 0; i < qnum_per_vf; i++) { + ae_handle->qs[i] = &ring_pair_cb->q; + ae_handle->qs[i]->rx_ring.q = ae_handle->qs[i]; + ae_handle->qs[i]->tx_ring.q = ae_handle->qs[i]; + + ring_pair_cb->used_by_vf = 1; + if (port_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) + ring_pair_cb->port_id_in_dsa = port_idx; + else + ring_pair_cb->port_id_in_dsa = 0; + + ring_pair_cb++; + } + + vf_cb->dsaf_dev = dsaf_dev; + vf_cb->port_index = port_idx; + vf_cb->mac_cb = &dsaf_dev->mac_cb[port_idx]; + + ae_handle->phy_if = vf_cb->mac_cb->phy_if; + ae_handle->phy_node = vf_cb->mac_cb->phy_node; + ae_handle->if_support = vf_cb->mac_cb->if_support; + ae_handle->port_type = vf_cb->mac_cb->mac_type; + + return ae_handle; +vf_id_err: + kfree(vf_cb); +handle_err: + return ae_handle; +} + +static void hns_ae_put_handle(struct hnae_handle *handle) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + int i; + + vf_cb->mac_cb = NULL; + + kfree(vf_cb); + + for (i = 0; i < handle->q_num; i++) + hns_ae_get_ring_pair(handle->qs[i])->used_by_vf = 0; +} + +static void hns_ae_ring_enable_all(struct hnae_handle *handle, int val) +{ + int q_num = handle->q_num; + int i; + + for (i = 0; i < q_num; i++) + hns_rcb_ring_enable_hw(handle->qs[i], val); +} + +static void hns_ae_init_queue(struct hnae_queue *q) +{ + struct ring_pair_cb *ring = + container_of(q, struct ring_pair_cb, q); + + hns_rcb_init_hw(ring); +} + +static void hns_ae_fini_queue(struct hnae_queue *q) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(q->handle); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_rcb_reset_ring_hw(q); +} + +static int hns_ae_set_mac_address(struct hnae_handle *handle, void *p) +{ + int ret; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + if (!p || !is_valid_ether_addr((const u8 *)p)) { + dev_err(handle->owner_dev, "is not valid ether addr !\n"); + return -EADDRNOTAVAIL; + } + + ret = hns_mac_change_vf_addr(mac_cb, handle->vf_id, p); + if (ret != 0) { + dev_err(handle->owner_dev, + "set_mac_address fail, ret=%d!\n", ret); + return ret; + } + + return 0; +} + +static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr) +{ + int ret; + char *mac_addr = (char *)addr; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + assert(mac_cb); + + if (mac_cb->mac_type != HNAE_PORT_SERVICE) + return 0; + + ret = hns_mac_set_multi(mac_cb, mac_cb->mac_id, mac_addr, ENABLE); + if (ret) { + dev_err(handle->owner_dev, + "mac add mul_mac:%pM port%d fail, ret = %#x!\n", + mac_addr, mac_cb->mac_id, ret); + return ret; + } + + ret = hns_mac_set_multi(mac_cb, DSAF_BASE_INNER_PORT_NUM, + mac_addr, ENABLE); + if (ret) + dev_err(handle->owner_dev, + "mac add mul_mac:%pM port%d fail, ret = %#x!\n", + mac_addr, DSAF_BASE_INNER_PORT_NUM, ret); + + return ret; +} + +static int hns_ae_set_mtu(struct hnae_handle *handle, int new_mtu) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + return hns_mac_set_mtu(mac_cb, new_mtu); +} + +static int hns_ae_start(struct hnae_handle *handle) +{ + int ret; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + ret = hns_mac_vm_config_bc_en(mac_cb, 0, ENABLE); + if (ret) + return ret; + + hns_ae_ring_enable_all(handle, 1); + msleep(100); + + hns_mac_start(mac_cb); + + return 0; +} + +void hns_ae_stop(struct hnae_handle *handle) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + /* just clean tx fbd, neednot rx fbd*/ + hns_rcb_wait_fbd_clean(handle->qs, handle->q_num, RCB_INT_FLAG_TX); + + msleep(20); + + hns_mac_stop(mac_cb); + + usleep_range(10000, 20000); + + hns_ae_ring_enable_all(handle, 0); + + (void)hns_mac_vm_config_bc_en(mac_cb, 0, DISABLE); +} + +static void hns_ae_reset(struct hnae_handle *handle) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_DEBUG) { + u8 ppe_common_index = + vf_cb->port_index - DSAF_SERVICE_PORT_NUM_PER_DSAF + 1; + + hns_mac_reset(vf_cb->mac_cb); + hns_ppe_reset_common(vf_cb->dsaf_dev, ppe_common_index); + } +} + +void hns_ae_toggle_ring_irq(struct hnae_ring *ring, u32 mask) +{ + u32 flag; + + if (is_tx_ring(ring)) + flag = RCB_INT_FLAG_TX; + else + flag = RCB_INT_FLAG_RX; + + hns_rcb_int_clr_hw(ring->q, flag); + hns_rcb_int_ctrl_hw(ring->q, flag, mask); +} + +static void hns_ae_toggle_queue_status(struct hnae_queue *queue, u32 val) +{ + hns_rcb_start(queue, val); +} + +static int hns_ae_get_link_status(struct hnae_handle *handle) +{ + u32 link_status; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + hns_mac_get_link_status(mac_cb, &link_status); + + return !!link_status; +} + +static int hns_ae_get_mac_info(struct hnae_handle *handle, + u8 *auto_neg, u16 *speed, u8 *duplex) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + return hns_mac_get_port_info(mac_cb, auto_neg, speed, duplex); +} + +static void hns_ae_adjust_link(struct hnae_handle *handle, int speed, + int duplex) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + hns_mac_adjust_link(mac_cb, speed, duplex); +} + +static void hns_ae_get_ring_bdnum_limit(struct hnae_queue *queue, + u32 *uplimit) +{ + *uplimit = HNS_RCB_RING_MAX_PENDING_BD; +} + +static void hns_ae_get_pauseparam(struct hnae_handle *handle, + u32 *auto_neg, u32 *rx_en, u32 *tx_en) +{ + assert(handle); + + hns_mac_get_autoneg(hns_get_mac_cb(handle), auto_neg); + + hns_mac_get_pauseparam(hns_get_mac_cb(handle), rx_en, tx_en); +} + +static int hns_ae_set_autoneg(struct hnae_handle *handle, u8 enable) +{ + assert(handle); + + return hns_mac_set_autoneg(hns_get_mac_cb(handle), enable); +} + +static int hns_ae_get_autoneg(struct hnae_handle *handle) +{ + u32 auto_neg; + + assert(handle); + + hns_mac_get_autoneg(hns_get_mac_cb(handle), &auto_neg); + + return auto_neg; +} + +static int hns_ae_set_pauseparam(struct hnae_handle *handle, + u32 autoneg, u32 rx_en, u32 tx_en) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + int ret; + + ret = hns_mac_set_autoneg(mac_cb, autoneg); + if (ret) + return ret; + + return hns_mac_set_pauseparam(mac_cb, rx_en, tx_en); +} + +static void hns_ae_get_coalesce_usecs(struct hnae_handle *handle, + u32 *tx_usecs, u32 *rx_usecs) +{ + int port; + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + *tx_usecs = hns_rcb_get_coalesce_usecs( + hns_ae_get_dsaf_dev(handle->dev), + hns_dsaf_get_comm_idx_by_port(port)); + *rx_usecs = hns_rcb_get_coalesce_usecs( + hns_ae_get_dsaf_dev(handle->dev), + hns_dsaf_get_comm_idx_by_port(port)); +} + +static void hns_ae_get_rx_max_coalesced_frames(struct hnae_handle *handle, + u32 *tx_frames, u32 *rx_frames) +{ + int port; + + assert(handle); + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + *tx_frames = hns_rcb_get_coalesced_frames( + hns_ae_get_dsaf_dev(handle->dev), port); + *rx_frames = hns_rcb_get_coalesced_frames( + hns_ae_get_dsaf_dev(handle->dev), port); +} + +static void hns_ae_set_coalesce_usecs(struct hnae_handle *handle, + u32 timeout) +{ + int port; + + assert(handle); + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + hns_rcb_set_coalesce_usecs(hns_ae_get_dsaf_dev(handle->dev), + port, timeout); +} + +static int hns_ae_set_coalesce_frames(struct hnae_handle *handle, + u32 coalesce_frames) +{ + int port; + int ret; + + assert(handle); + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + ret = hns_rcb_set_coalesced_frames(hns_ae_get_dsaf_dev(handle->dev), + port, coalesce_frames); + return ret; +} + +void hns_ae_update_stats(struct hnae_handle *handle, + struct net_device_stats *net_stats) +{ + int port; + int idx; + struct dsaf_device *dsaf_dev; + struct hns_mac_cb *mac_cb; + struct hns_ppe_cb *ppe_cb; + struct hnae_queue *queue; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + u64 tx_bytes = 0, rx_bytes = 0, tx_packets = 0, rx_packets = 0; + u64 rx_errors = 0, tx_errors = 0, tx_dropped = 0; + u64 rx_missed_errors = 0; + + dsaf_dev = hns_ae_get_dsaf_dev(handle->dev); + if (!dsaf_dev) + return; + port = vf_cb->port_index; + ppe_cb = hns_get_ppe_cb(handle); + mac_cb = hns_get_mac_cb(handle); + + for (idx = 0; idx < handle->q_num; idx++) { + queue = handle->qs[idx]; + hns_rcb_update_stats(queue); + + tx_bytes += queue->tx_ring.stats.tx_bytes; + tx_packets += queue->tx_ring.stats.tx_pkts; + rx_bytes += queue->rx_ring.stats.rx_bytes; + rx_packets += queue->rx_ring.stats.rx_pkts; + + rx_errors += queue->rx_ring.stats.err_pkt_len + + queue->rx_ring.stats.l2_err + + queue->rx_ring.stats.l3l4_csum_err; + } + + hns_ppe_update_stats(ppe_cb); + rx_missed_errors = ppe_cb->hw_stats.rx_drop_no_buf; + tx_errors += ppe_cb->hw_stats.tx_err_checksum + + ppe_cb->hw_stats.tx_err_fifo_empty; + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) { + hns_dsaf_update_stats(dsaf_dev, port); + /* for port upline direction, i.e., rx. */ + rx_missed_errors += dsaf_dev->hw_stats[port].bp_drop; + rx_missed_errors += dsaf_dev->hw_stats[port].pad_drop; + rx_missed_errors += dsaf_dev->hw_stats[port].crc_false; + + /* for port downline direction, i.e., tx. */ + port = port + DSAF_PPE_INODE_BASE; + hns_dsaf_update_stats(dsaf_dev, port); + tx_dropped += dsaf_dev->hw_stats[port].bp_drop; + tx_dropped += dsaf_dev->hw_stats[port].pad_drop; + tx_dropped += dsaf_dev->hw_stats[port].crc_false; + tx_dropped += dsaf_dev->hw_stats[port].rslt_drop; + tx_dropped += dsaf_dev->hw_stats[port].vlan_drop; + tx_dropped += dsaf_dev->hw_stats[port].stp_drop; + } + + hns_mac_update_stats(mac_cb); + rx_errors += mac_cb->hw_stats.rx_fifo_overrun_err; + + tx_errors += mac_cb->hw_stats.tx_bad_pkts + + mac_cb->hw_stats.tx_fragment_err + + mac_cb->hw_stats.tx_jabber_err + + mac_cb->hw_stats.tx_underrun_err + + mac_cb->hw_stats.tx_crc_err; + + net_stats->tx_bytes = tx_bytes; + net_stats->tx_packets = tx_packets; + net_stats->rx_bytes = rx_bytes; + net_stats->rx_dropped = 0; + net_stats->rx_packets = rx_packets; + net_stats->rx_errors = rx_errors; + net_stats->tx_errors = tx_errors; + net_stats->tx_dropped = tx_dropped; + net_stats->rx_missed_errors = rx_missed_errors; + net_stats->rx_crc_errors = mac_cb->hw_stats.rx_fcs_err; + net_stats->rx_frame_errors = mac_cb->hw_stats.rx_align_err; + net_stats->rx_fifo_errors = mac_cb->hw_stats.rx_fifo_overrun_err; + net_stats->rx_length_errors = mac_cb->hw_stats.rx_len_err; + net_stats->multicast = mac_cb->hw_stats.rx_mc_pkts; +} + +void hns_ae_get_stats(struct hnae_handle *handle, u64 *data) +{ + int idx; + struct hns_mac_cb *mac_cb; + struct hns_ppe_cb *ppe_cb; + u64 *p = data; + struct hnae_vf_cb *vf_cb; + + if (!handle || !data) { + pr_err("hns_ae_get_stats NULL handle or data pointer!\n"); + return; + } + + vf_cb = hns_ae_get_vf_cb(handle); + mac_cb = hns_get_mac_cb(handle); + ppe_cb = hns_get_ppe_cb(handle); + + for (idx = 0; idx < handle->q_num; idx++) { + hns_rcb_get_stats(handle->qs[idx], p); + p += hns_rcb_get_ring_sset_count((int)ETH_SS_STATS); + } + + hns_ppe_get_stats(ppe_cb, p); + p += hns_ppe_get_sset_count((int)ETH_SS_STATS); + + hns_mac_get_stats(mac_cb, p); + p += hns_mac_get_sset_count(mac_cb, (int)ETH_SS_STATS); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_dsaf_get_stats(vf_cb->dsaf_dev, p, vf_cb->port_index); +} + +void hns_ae_get_strings(struct hnae_handle *handle, + u32 stringset, u8 *data) +{ + int port; + int idx; + struct hns_mac_cb *mac_cb; + struct hns_ppe_cb *ppe_cb; + u8 *p = data; + struct hnae_vf_cb *vf_cb; + + assert(handle); + + vf_cb = hns_ae_get_vf_cb(handle); + port = vf_cb->port_index; + mac_cb = hns_get_mac_cb(handle); + ppe_cb = hns_get_ppe_cb(handle); + + for (idx = 0; idx < handle->q_num; idx++) { + hns_rcb_get_strings(stringset, p, idx); + p += ETH_GSTRING_LEN * hns_rcb_get_ring_sset_count(stringset); + } + + hns_ppe_get_strings(ppe_cb, stringset, p); + p += ETH_GSTRING_LEN * hns_ppe_get_sset_count(stringset); + + hns_mac_get_strings(mac_cb, stringset, p); + p += ETH_GSTRING_LEN * hns_mac_get_sset_count(mac_cb, stringset); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_dsaf_get_strings(stringset, p, port); +} + +int hns_ae_get_sset_count(struct hnae_handle *handle, int stringset) +{ + u32 sset_count = 0; + struct hns_mac_cb *mac_cb; + + assert(handle); + + mac_cb = hns_get_mac_cb(handle); + + sset_count += hns_rcb_get_ring_sset_count(stringset) * handle->q_num; + sset_count += hns_ppe_get_sset_count(stringset); + sset_count += hns_mac_get_sset_count(mac_cb, stringset); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + sset_count += hns_dsaf_get_sset_count(stringset); + + return sset_count; +} + +static int hns_ae_config_loopback(struct hnae_handle *handle, + enum hnae_loop loop, int en) +{ + int ret; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + switch (loop) { + case MAC_INTERNALLOOP_SERDES: + ret = hns_mac_config_sds_loopback(vf_cb->mac_cb, en); + break; + case MAC_INTERNALLOOP_MAC: + ret = hns_mac_config_mac_loopback(vf_cb->mac_cb, loop, en); + break; + default: + ret = -EINVAL; + } + return ret; +} + +void hns_ae_update_led_status(struct hnae_handle *handle) +{ + struct hns_mac_cb *mac_cb; + + assert(handle); + mac_cb = hns_get_mac_cb(handle); + if (!mac_cb->cpld_vaddr) + return; + hns_set_led_opt(mac_cb); +} + +int hns_ae_cpld_set_led_id(struct hnae_handle *handle, + enum hnae_led_state status) +{ + struct hns_mac_cb *mac_cb; + + assert(handle); + + mac_cb = hns_get_mac_cb(handle); + + return hns_cpld_led_set_id(mac_cb, status); +} + +void hns_ae_get_regs(struct hnae_handle *handle, void *data) +{ + u32 *p = data; + u32 rcb_com_idx; + int i; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle); + + hns_ppe_get_regs(ppe_cb, p); + p += hns_ppe_get_regs_count(); + + rcb_com_idx = hns_dsaf_get_comm_idx_by_port(vf_cb->port_index); + hns_rcb_get_common_regs(vf_cb->dsaf_dev->rcb_common[rcb_com_idx], p); + p += hns_rcb_get_common_regs_count(); + + for (i = 0; i < handle->q_num; i++) { + hns_rcb_get_ring_regs(handle->qs[i], p); + p += hns_rcb_get_ring_regs_count(); + } + + hns_mac_get_regs(vf_cb->mac_cb, p); + p += hns_mac_get_regs_count(vf_cb->mac_cb); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_dsaf_get_regs(vf_cb->dsaf_dev, vf_cb->port_index, p); +} + +int hns_ae_get_regs_len(struct hnae_handle *handle) +{ + u32 total_num; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + total_num = hns_ppe_get_regs_count(); + total_num += hns_rcb_get_common_regs_count(); + total_num += hns_rcb_get_ring_regs_count() * handle->q_num; + total_num += hns_mac_get_regs_count(vf_cb->mac_cb); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE) + total_num += hns_dsaf_get_regs_count(); + + return total_num; +} + +static struct hnae_ae_ops hns_dsaf_ops = { + .get_handle = hns_ae_get_handle, + .put_handle = hns_ae_put_handle, + .init_queue = hns_ae_init_queue, + .fini_queue = hns_ae_fini_queue, + .start = hns_ae_start, + .stop = hns_ae_stop, + .reset = hns_ae_reset, + .toggle_ring_irq = hns_ae_toggle_ring_irq, + .toggle_queue_status = hns_ae_toggle_queue_status, + .get_status = hns_ae_get_link_status, + .get_info = hns_ae_get_mac_info, + .adjust_link = hns_ae_adjust_link, + .set_loopback = hns_ae_config_loopback, + .get_ring_bdnum_limit = hns_ae_get_ring_bdnum_limit, + .get_pauseparam = hns_ae_get_pauseparam, + .set_autoneg = hns_ae_set_autoneg, + .get_autoneg = hns_ae_get_autoneg, + .set_pauseparam = hns_ae_set_pauseparam, + .get_coalesce_usecs = hns_ae_get_coalesce_usecs, + .get_rx_max_coalesced_frames = hns_ae_get_rx_max_coalesced_frames, + .set_coalesce_usecs = hns_ae_set_coalesce_usecs, + .set_coalesce_frames = hns_ae_set_coalesce_frames, + .set_mac_addr = hns_ae_set_mac_address, + .set_mc_addr = hns_ae_set_multicast_one, + .set_mtu = hns_ae_set_mtu, + .update_stats = hns_ae_update_stats, + .get_stats = hns_ae_get_stats, + .get_strings = hns_ae_get_strings, + .get_sset_count = hns_ae_get_sset_count, + .update_led_status = hns_ae_update_led_status, + .set_led_id = hns_ae_cpld_set_led_id, + .get_regs = hns_ae_get_regs, + .get_regs_len = hns_ae_get_regs_len +}; + +int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev) +{ + struct hnae_ae_dev *ae_dev = &dsaf_dev->ae_dev; + + ae_dev->ops = &hns_dsaf_ops; + ae_dev->dev = dsaf_dev->dev; + + return hnae_ae_register(ae_dev, THIS_MODULE); +} + +void hns_dsaf_ae_uninit(struct dsaf_device *dsaf_dev) +{ + hnae_ae_unregister(&dsaf_dev->ae_dev); +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c new file mode 100644 index 000000000000..b8517b00e706 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c @@ -0,0 +1,704 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/delay.h> +#include <linux/of_mdio.h> +#include "hns_dsaf_main.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_gmac.h" + +static const struct mac_stats_string g_gmac_stats_string[] = { + {"gmac_rx_octets_total_ok", MAC_STATS_FIELD_OFF(rx_good_bytes)}, + {"gmac_rx_octets_bad", MAC_STATS_FIELD_OFF(rx_bad_bytes)}, + {"gmac_rx_uc_pkts", MAC_STATS_FIELD_OFF(rx_uc_pkts)}, + {"gamc_rx_mc_pkts", MAC_STATS_FIELD_OFF(rx_mc_pkts)}, + {"gmac_rx_bc_pkts", MAC_STATS_FIELD_OFF(rx_bc_pkts)}, + {"gmac_rx_pkts_64octets", MAC_STATS_FIELD_OFF(rx_64bytes)}, + {"gmac_rx_pkts_65to127", MAC_STATS_FIELD_OFF(rx_65to127)}, + {"gmac_rx_pkts_128to255", MAC_STATS_FIELD_OFF(rx_128to255)}, + {"gmac_rx_pkts_256to511", MAC_STATS_FIELD_OFF(rx_256to511)}, + {"gmac_rx_pkts_512to1023", MAC_STATS_FIELD_OFF(rx_512to1023)}, + {"gmac_rx_pkts_1024to1518", MAC_STATS_FIELD_OFF(rx_1024to1518)}, + {"gmac_rx_pkts_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax)}, + {"gmac_rx_fcs_errors", MAC_STATS_FIELD_OFF(rx_fcs_err)}, + {"gmac_rx_tagged", MAC_STATS_FIELD_OFF(rx_vlan_pkts)}, + {"gmac_rx_data_err", MAC_STATS_FIELD_OFF(rx_data_err)}, + {"gmac_rx_align_errors", MAC_STATS_FIELD_OFF(rx_align_err)}, + {"gmac_rx_long_errors", MAC_STATS_FIELD_OFF(rx_oversize)}, + {"gmac_rx_jabber_errors", MAC_STATS_FIELD_OFF(rx_jabber_err)}, + {"gmac_rx_pause_maccontrol", MAC_STATS_FIELD_OFF(rx_pfc_tc0)}, + {"gmac_rx_unknown_maccontrol", MAC_STATS_FIELD_OFF(rx_unknown_ctrl)}, + {"gmac_rx_very_long_err", MAC_STATS_FIELD_OFF(rx_long_err)}, + {"gmac_rx_runt_err", MAC_STATS_FIELD_OFF(rx_minto64)}, + {"gmac_rx_short_err", MAC_STATS_FIELD_OFF(rx_under_min)}, + {"gmac_rx_filt_pkt", MAC_STATS_FIELD_OFF(rx_filter_bytes)}, + {"gmac_rx_octets_total_filt", MAC_STATS_FIELD_OFF(rx_filter_pkts)}, + {"gmac_rx_overrun_cnt", MAC_STATS_FIELD_OFF(rx_fifo_overrun_err)}, + {"gmac_rx_length_err", MAC_STATS_FIELD_OFF(rx_len_err)}, + {"gmac_rx_fail_comma", MAC_STATS_FIELD_OFF(rx_comma_err)}, + + {"gmac_tx_octets_ok", MAC_STATS_FIELD_OFF(tx_good_bytes)}, + {"gmac_tx_octets_bad", MAC_STATS_FIELD_OFF(tx_bad_bytes)}, + {"gmac_tx_uc_pkts", MAC_STATS_FIELD_OFF(tx_uc_pkts)}, + {"gmac_tx_mc_pkts", MAC_STATS_FIELD_OFF(tx_mc_pkts)}, + {"gmac_tx_bc_pkts", MAC_STATS_FIELD_OFF(tx_bc_pkts)}, + {"gmac_tx_pkts_64octets", MAC_STATS_FIELD_OFF(tx_64bytes)}, + {"gmac_tx_pkts_65to127", MAC_STATS_FIELD_OFF(tx_65to127)}, + {"gmac_tx_pkts_128to255", MAC_STATS_FIELD_OFF(tx_128to255)}, + {"gmac_tx_pkts_256to511", MAC_STATS_FIELD_OFF(tx_256to511)}, + {"gmac_tx_pkts_512to1023", MAC_STATS_FIELD_OFF(tx_512to1023)}, + {"gmac_tx_pkts_1024to1518", MAC_STATS_FIELD_OFF(tx_1024to1518)}, + {"gmac_tx_pkts_1519tomax", MAC_STATS_FIELD_OFF(tx_1519tomax)}, + {"gmac_tx_excessive_length_drop", MAC_STATS_FIELD_OFF(tx_jabber_err)}, + {"gmac_tx_underrun", MAC_STATS_FIELD_OFF(tx_underrun_err)}, + {"gmac_tx_tagged", MAC_STATS_FIELD_OFF(tx_vlan)}, + {"gmac_tx_crc_error", MAC_STATS_FIELD_OFF(tx_crc_err)}, + {"gmac_tx_pause_frames", MAC_STATS_FIELD_OFF(tx_pfc_tc0)} +}; + +static void hns_gmac_enable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + /*enable GE rX/tX */ + if ((mode == MAC_COMM_MODE_TX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_TX_EN_B, 1); + + if ((mode == MAC_COMM_MODE_RX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 1); +} + +static void hns_gmac_disable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + /*disable GE rX/tX */ + if ((mode == MAC_COMM_MODE_TX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_TX_EN_B, 0); + + if ((mode == MAC_COMM_MODE_RX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 0); +} + +/** +*hns_gmac_get_en - get port enable +*@mac_drv:mac device +*@rx:rx enable +*@tx:tx enable +*/ +static void hns_gmac_get_en(void *mac_drv, u32 *rx, u32 *tx) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 porten; + + porten = dsaf_read_dev(drv, GMAC_PORT_EN_REG); + *tx = dsaf_get_bit(porten, GMAC_PORT_TX_EN_B); + *rx = dsaf_get_bit(porten, GMAC_PORT_RX_EN_B); +} + +static void hns_gmac_free(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + + u32 mac_id = drv->mac_id; + + hns_dsaf_ge_srst_by_port(dsaf_dev, mac_id, 0); +} + +static void hns_gmac_set_tx_auto_pause_frames(void *mac_drv, u16 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_field(drv, GMAC_FC_TX_TIMER_REG, GMAC_FC_TX_TIMER_M, + GMAC_FC_TX_TIMER_S, newval); +} + +static void hns_gmac_get_tx_auto_pause_frames(void *mac_drv, u16 *newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *newval = dsaf_get_dev_field(drv, GMAC_FC_TX_TIMER_REG, + GMAC_FC_TX_TIMER_M, GMAC_FC_TX_TIMER_S); +} + +static void hns_gmac_set_rx_auto_pause_frames(void *mac_drv, u32 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_PAUSE_EN_REG, + GMAC_PAUSE_EN_RX_FDFC_B, !!newval); +} + +static void hns_gmac_config_max_frame_length(void *mac_drv, u16 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_field(drv, GMAC_MAX_FRM_SIZE_REG, GMAC_MAX_FRM_SIZE_M, + GMAC_MAX_FRM_SIZE_S, newval); + + dsaf_set_dev_field(drv, GAMC_RX_MAX_FRAME, GMAC_MAX_FRM_SIZE_M, + GMAC_MAX_FRM_SIZE_S, newval); +} + +static void hns_gmac_config_an_mode(void *mac_drv, u8 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_TRANSMIT_CONTROL_REG, + GMAC_TX_AN_EN_B, !!newval); +} + +static void hns_gmac_tx_loop_pkt_dis(void *mac_drv) +{ + u32 tx_loop_pkt_pri; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + tx_loop_pkt_pri = dsaf_read_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG); + dsaf_set_bit(tx_loop_pkt_pri, GMAC_TX_LOOP_PKT_EN_B, 1); + dsaf_set_bit(tx_loop_pkt_pri, GMAC_TX_LOOP_PKT_HIG_PRI_B, 0); + dsaf_write_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG, tx_loop_pkt_pri); +} + +static void hns_gmac_set_duplex_type(void *mac_drv, u8 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_DUPLEX_TYPE_REG, + GMAC_DUPLEX_TYPE_B, !!newval); +} + +static void hns_gmac_get_duplex_type(void *mac_drv, + enum hns_gmac_duplex_mdoe *duplex_mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *duplex_mode = (enum hns_gmac_duplex_mdoe)dsaf_get_dev_bit( + drv, GMAC_DUPLEX_TYPE_REG, GMAC_DUPLEX_TYPE_B); +} + +static void hns_gmac_get_port_mode(void *mac_drv, enum hns_port_mode *port_mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *port_mode = (enum hns_port_mode)dsaf_get_dev_field( + drv, GMAC_PORT_MODE_REG, GMAC_PORT_MODE_M, GMAC_PORT_MODE_S); +} + +static void hns_gmac_port_mode_get(void *mac_drv, + struct hns_gmac_port_mode_cfg *port_mode) +{ + u32 tx_ctrl; + u32 recv_ctrl; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + port_mode->port_mode = (enum hns_port_mode)dsaf_get_dev_field( + drv, GMAC_PORT_MODE_REG, GMAC_PORT_MODE_M, GMAC_PORT_MODE_S); + + tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + recv_ctrl = dsaf_read_dev(drv, GMAC_RECV_CONTROL_REG); + + port_mode->max_frm_size = + dsaf_get_dev_field(drv, GMAC_MAX_FRM_SIZE_REG, + GMAC_MAX_FRM_SIZE_M, GMAC_MAX_FRM_SIZE_S); + port_mode->short_runts_thr = + dsaf_get_dev_field(drv, GMAC_SHORT_RUNTS_THR_REG, + GMAC_SHORT_RUNTS_THR_M, + GMAC_SHORT_RUNTS_THR_S); + + port_mode->pad_enable = dsaf_get_bit(tx_ctrl, GMAC_TX_PAD_EN_B); + port_mode->crc_add = dsaf_get_bit(tx_ctrl, GMAC_TX_CRC_ADD_B); + port_mode->an_enable = dsaf_get_bit(tx_ctrl, GMAC_TX_AN_EN_B); + + port_mode->runt_pkt_en = + dsaf_get_bit(recv_ctrl, GMAC_RECV_CTRL_RUNT_PKT_EN_B); + port_mode->strip_pad_en = + dsaf_get_bit(recv_ctrl, GMAC_RECV_CTRL_STRIP_PAD_EN_B); +} + +static void hns_gmac_pause_frm_cfg(void *mac_drv, u32 rx_pause_en, + u32 tx_pause_en) +{ + u32 pause_en; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + pause_en = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG); + dsaf_set_bit(pause_en, GMAC_PAUSE_EN_RX_FDFC_B, !!rx_pause_en); + dsaf_set_bit(pause_en, GMAC_PAUSE_EN_TX_FDFC_B, !!tx_pause_en); + dsaf_write_dev(drv, GMAC_PAUSE_EN_REG, pause_en); +} + +static void hns_gmac_get_pausefrm_cfg(void *mac_drv, u32 *rx_pause_en, + u32 *tx_pause_en) +{ + u32 pause_en; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + pause_en = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG); + + *rx_pause_en = dsaf_get_bit(pause_en, GMAC_PAUSE_EN_RX_FDFC_B); + *tx_pause_en = dsaf_get_bit(pause_en, GMAC_PAUSE_EN_TX_FDFC_B); +} + +static int hns_gmac_adjust_link(void *mac_drv, enum mac_speed speed, + u32 full_duplex) +{ + u32 tx_ctrl; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_DUPLEX_TYPE_REG, + GMAC_DUPLEX_TYPE_B, !!full_duplex); + + switch (speed) { + case MAC_SPEED_10: + dsaf_set_dev_field( + drv, GMAC_PORT_MODE_REG, + GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x6); + break; + case MAC_SPEED_100: + dsaf_set_dev_field( + drv, GMAC_PORT_MODE_REG, + GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x7); + break; + case MAC_SPEED_1000: + dsaf_set_dev_field( + drv, GMAC_PORT_MODE_REG, + GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x8); + break; + default: + dev_err(drv->dev, + "hns_gmac_adjust_link fail, speed%d mac%d\n", + speed, drv->mac_id); + return -EINVAL; + } + + tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + dsaf_set_bit(tx_ctrl, GMAC_TX_PAD_EN_B, 1); + dsaf_set_bit(tx_ctrl, GMAC_TX_CRC_ADD_B, 1); + dsaf_write_dev(drv, GMAC_TRANSMIT_CONTROL_REG, tx_ctrl); + + dsaf_set_dev_bit(drv, GMAC_MODE_CHANGE_EN_REG, + GMAC_MODE_CHANGE_EB_B, 1); + + return 0; +} + +static void hns_gmac_init(void *mac_drv) +{ + u32 port; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + + port = drv->mac_id; + + hns_dsaf_ge_srst_by_port(dsaf_dev, port, 0); + mdelay(10); + hns_dsaf_ge_srst_by_port(dsaf_dev, port, 1); + mdelay(10); + hns_gmac_disable(mac_drv, MAC_COMM_MODE_RX_AND_TX); + hns_gmac_tx_loop_pkt_dis(mac_drv); +} + +void hns_gmac_update_stats(void *mac_drv) +{ + struct mac_hw_stats *hw_stats = NULL; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + hw_stats = &drv->mac_cb->hw_stats; + + /* RX */ + hw_stats->rx_good_bytes + += dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_OK_REG); + hw_stats->rx_bad_bytes + += dsaf_read_dev(drv, GMAC_RX_OCTETS_BAD_REG); + hw_stats->rx_uc_pkts += dsaf_read_dev(drv, GMAC_RX_UC_PKTS_REG); + hw_stats->rx_mc_pkts += dsaf_read_dev(drv, GMAC_RX_MC_PKTS_REG); + hw_stats->rx_bc_pkts += dsaf_read_dev(drv, GMAC_RX_BC_PKTS_REG); + hw_stats->rx_64bytes + += dsaf_read_dev(drv, GMAC_RX_PKTS_64OCTETS_REG); + hw_stats->rx_65to127 + += dsaf_read_dev(drv, GMAC_RX_PKTS_65TO127OCTETS_REG); + hw_stats->rx_128to255 + += dsaf_read_dev(drv, GMAC_RX_PKTS_128TO255OCTETS_REG); + hw_stats->rx_256to511 + += dsaf_read_dev(drv, GMAC_RX_PKTS_255TO511OCTETS_REG); + hw_stats->rx_512to1023 + += dsaf_read_dev(drv, GMAC_RX_PKTS_512TO1023OCTETS_REG); + hw_stats->rx_1024to1518 + += dsaf_read_dev(drv, GMAC_RX_PKTS_1024TO1518OCTETS_REG); + hw_stats->rx_1519tomax + += dsaf_read_dev(drv, GMAC_RX_PKTS_1519TOMAXOCTETS_REG); + hw_stats->rx_fcs_err += dsaf_read_dev(drv, GMAC_RX_FCS_ERRORS_REG); + hw_stats->rx_vlan_pkts += dsaf_read_dev(drv, GMAC_RX_TAGGED_REG); + hw_stats->rx_data_err += dsaf_read_dev(drv, GMAC_RX_DATA_ERR_REG); + hw_stats->rx_align_err + += dsaf_read_dev(drv, GMAC_RX_ALIGN_ERRORS_REG); + hw_stats->rx_oversize + += dsaf_read_dev(drv, GMAC_RX_LONG_ERRORS_REG); + hw_stats->rx_jabber_err + += dsaf_read_dev(drv, GMAC_RX_JABBER_ERRORS_REG); + hw_stats->rx_pfc_tc0 + += dsaf_read_dev(drv, GMAC_RX_PAUSE_MACCTRL_FRAM_REG); + hw_stats->rx_unknown_ctrl + += dsaf_read_dev(drv, GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG); + hw_stats->rx_long_err + += dsaf_read_dev(drv, GMAC_RX_VERY_LONG_ERR_CNT_REG); + hw_stats->rx_minto64 + += dsaf_read_dev(drv, GMAC_RX_RUNT_ERR_CNT_REG); + hw_stats->rx_under_min + += dsaf_read_dev(drv, GMAC_RX_SHORT_ERR_CNT_REG); + hw_stats->rx_filter_pkts + += dsaf_read_dev(drv, GMAC_RX_FILT_PKT_CNT_REG); + hw_stats->rx_filter_bytes + += dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_FILT_REG); + hw_stats->rx_fifo_overrun_err + += dsaf_read_dev(drv, GMAC_RX_OVERRUN_CNT_REG); + hw_stats->rx_len_err + += dsaf_read_dev(drv, GMAC_RX_LENGTHFIELD_ERR_CNT_REG); + hw_stats->rx_comma_err + += dsaf_read_dev(drv, GMAC_RX_FAIL_COMMA_CNT_REG); + + /* TX */ + hw_stats->tx_good_bytes + += dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_OK_REG); + hw_stats->tx_bad_bytes + += dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_BAD_REG); + hw_stats->tx_uc_pkts += dsaf_read_dev(drv, GMAC_TX_UC_PKTS_REG); + hw_stats->tx_mc_pkts += dsaf_read_dev(drv, GMAC_TX_MC_PKTS_REG); + hw_stats->tx_bc_pkts += dsaf_read_dev(drv, GMAC_TX_BC_PKTS_REG); + hw_stats->tx_64bytes + += dsaf_read_dev(drv, GMAC_TX_PKTS_64OCTETS_REG); + hw_stats->tx_65to127 + += dsaf_read_dev(drv, GMAC_TX_PKTS_65TO127OCTETS_REG); + hw_stats->tx_128to255 + += dsaf_read_dev(drv, GMAC_TX_PKTS_128TO255OCTETS_REG); + hw_stats->tx_256to511 + += dsaf_read_dev(drv, GMAC_TX_PKTS_255TO511OCTETS_REG); + hw_stats->tx_512to1023 + += dsaf_read_dev(drv, GMAC_TX_PKTS_512TO1023OCTETS_REG); + hw_stats->tx_1024to1518 + += dsaf_read_dev(drv, GMAC_TX_PKTS_1024TO1518OCTETS_REG); + hw_stats->tx_1519tomax + += dsaf_read_dev(drv, GMAC_TX_PKTS_1519TOMAXOCTETS_REG); + hw_stats->tx_jabber_err + += dsaf_read_dev(drv, GMAC_TX_EXCESSIVE_LENGTH_DROP_REG); + hw_stats->tx_underrun_err + += dsaf_read_dev(drv, GMAC_TX_UNDERRUN_REG); + hw_stats->tx_vlan += dsaf_read_dev(drv, GMAC_TX_TAGGED_REG); + hw_stats->tx_crc_err += dsaf_read_dev(drv, GMAC_TX_CRC_ERROR_REG); + hw_stats->tx_pfc_tc0 + += dsaf_read_dev(drv, GMAC_TX_PAUSE_FRAMES_REG); +} + +static void hns_gmac_set_mac_addr(void *mac_drv, char *mac_addr) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + if (drv->mac_id >= DSAF_SERVICE_NW_NUM) { + u32 high_val = mac_addr[1] | (mac_addr[0] << 8); + + u32 low_val = mac_addr[5] | (mac_addr[4] << 8) + | (mac_addr[3] << 16) | (mac_addr[2] << 24); + dsaf_write_dev(drv, GMAC_STATION_ADDR_LOW_2_REG, low_val); + dsaf_write_dev(drv, GMAC_STATION_ADDR_HIGH_2_REG, high_val); + } +} + +static int hns_gmac_config_loopback(void *mac_drv, enum hnae_loop loop_mode, + u8 enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + switch (loop_mode) { + case MAC_INTERNALLOOP_MAC: + dsaf_set_dev_bit(drv, GMAC_LOOP_REG, GMAC_LP_REG_CF2MI_LP_EN_B, + !!enable); + break; + default: + dev_err(drv->dev, "loop_mode error\n"); + return -EINVAL; + } + + return 0; +} + +static void hns_gmac_config_pad_and_crc(void *mac_drv, u8 newval) +{ + u32 tx_ctrl; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + dsaf_set_bit(tx_ctrl, GMAC_TX_PAD_EN_B, !!newval); + dsaf_set_bit(tx_ctrl, GMAC_TX_CRC_ADD_B, !!newval); + dsaf_write_dev(drv, GMAC_TRANSMIT_CONTROL_REG, tx_ctrl); +} + +static void hns_gmac_get_id(void *mac_drv, u8 *mac_id) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *mac_id = drv->mac_id; +} + +static void hns_gmac_get_info(void *mac_drv, struct mac_info *mac_info) +{ + enum hns_gmac_duplex_mdoe duplex; + enum hns_port_mode speed; + u32 rx_pause; + u32 tx_pause; + u32 rx; + u32 tx; + u16 fc_tx_timer; + struct hns_gmac_port_mode_cfg port_mode = { GMAC_10M_MII, 0 }; + + hns_gmac_port_mode_get(mac_drv, &port_mode); + mac_info->pad_and_crc_en = port_mode.crc_add && port_mode.pad_enable; + mac_info->auto_neg = port_mode.an_enable; + + hns_gmac_get_tx_auto_pause_frames(mac_drv, &fc_tx_timer); + mac_info->tx_pause_time = fc_tx_timer; + + hns_gmac_get_en(mac_drv, &rx, &tx); + mac_info->port_en = rx && tx; + + hns_gmac_get_duplex_type(mac_drv, &duplex); + mac_info->duplex = duplex; + + hns_gmac_get_port_mode(mac_drv, &speed); + switch (speed) { + case GMAC_10M_SGMII: + mac_info->speed = MAC_SPEED_10; + break; + case GMAC_100M_SGMII: + mac_info->speed = MAC_SPEED_100; + break; + case GMAC_1000M_SGMII: + mac_info->speed = MAC_SPEED_1000; + break; + default: + mac_info->speed = 0; + break; + } + + hns_gmac_get_pausefrm_cfg(mac_drv, &rx_pause, &tx_pause); + mac_info->rx_pause_en = rx_pause; + mac_info->tx_pause_en = tx_pause; +} + +static void hns_gmac_autoneg_stat(void *mac_drv, u32 *enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *enable = dsaf_get_dev_bit(drv, GMAC_TRANSMIT_CONTROL_REG, + GMAC_TX_AN_EN_B); +} + +static void hns_gmac_get_link_status(void *mac_drv, u32 *link_stat) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *link_stat = dsaf_get_dev_bit(drv, GMAC_AN_NEG_STATE_REG, + GMAC_AN_NEG_STAT_RX_SYNC_OK_B); +} + +static void hns_gmac_get_regs(void *mac_drv, void *data) +{ + u32 *regs = data; + int i; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + /* base config registers */ + regs[0] = dsaf_read_dev(drv, GMAC_DUPLEX_TYPE_REG); + regs[1] = dsaf_read_dev(drv, GMAC_FD_FC_TYPE_REG); + regs[2] = dsaf_read_dev(drv, GMAC_FC_TX_TIMER_REG); + regs[3] = dsaf_read_dev(drv, GMAC_FD_FC_ADDR_LOW_REG); + regs[4] = dsaf_read_dev(drv, GMAC_FD_FC_ADDR_HIGH_REG); + regs[5] = dsaf_read_dev(drv, GMAC_IPG_TX_TIMER_REG); + regs[6] = dsaf_read_dev(drv, GMAC_PAUSE_THR_REG); + regs[7] = dsaf_read_dev(drv, GMAC_MAX_FRM_SIZE_REG); + regs[8] = dsaf_read_dev(drv, GMAC_PORT_MODE_REG); + regs[9] = dsaf_read_dev(drv, GMAC_PORT_EN_REG); + regs[10] = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG); + regs[11] = dsaf_read_dev(drv, GMAC_SHORT_RUNTS_THR_REG); + regs[12] = dsaf_read_dev(drv, GMAC_AN_NEG_STATE_REG); + regs[13] = dsaf_read_dev(drv, GMAC_TX_LOCAL_PAGE_REG); + regs[14] = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + regs[15] = dsaf_read_dev(drv, GMAC_REC_FILT_CONTROL_REG); + regs[16] = dsaf_read_dev(drv, GMAC_PTP_CONFIG_REG); + + /* rx static registers */ + regs[17] = dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_OK_REG); + regs[18] = dsaf_read_dev(drv, GMAC_RX_OCTETS_BAD_REG); + regs[19] = dsaf_read_dev(drv, GMAC_RX_UC_PKTS_REG); + regs[20] = dsaf_read_dev(drv, GMAC_RX_MC_PKTS_REG); + regs[21] = dsaf_read_dev(drv, GMAC_RX_BC_PKTS_REG); + regs[22] = dsaf_read_dev(drv, GMAC_RX_PKTS_64OCTETS_REG); + regs[23] = dsaf_read_dev(drv, GMAC_RX_PKTS_65TO127OCTETS_REG); + regs[24] = dsaf_read_dev(drv, GMAC_RX_PKTS_128TO255OCTETS_REG); + regs[25] = dsaf_read_dev(drv, GMAC_RX_PKTS_255TO511OCTETS_REG); + regs[26] = dsaf_read_dev(drv, GMAC_RX_PKTS_512TO1023OCTETS_REG); + regs[27] = dsaf_read_dev(drv, GMAC_RX_PKTS_1024TO1518OCTETS_REG); + regs[28] = dsaf_read_dev(drv, GMAC_RX_PKTS_1519TOMAXOCTETS_REG); + regs[29] = dsaf_read_dev(drv, GMAC_RX_FCS_ERRORS_REG); + regs[30] = dsaf_read_dev(drv, GMAC_RX_TAGGED_REG); + regs[31] = dsaf_read_dev(drv, GMAC_RX_DATA_ERR_REG); + regs[32] = dsaf_read_dev(drv, GMAC_RX_ALIGN_ERRORS_REG); + regs[33] = dsaf_read_dev(drv, GMAC_RX_LONG_ERRORS_REG); + regs[34] = dsaf_read_dev(drv, GMAC_RX_JABBER_ERRORS_REG); + regs[35] = dsaf_read_dev(drv, GMAC_RX_PAUSE_MACCTRL_FRAM_REG); + regs[36] = dsaf_read_dev(drv, GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG); + regs[37] = dsaf_read_dev(drv, GMAC_RX_VERY_LONG_ERR_CNT_REG); + regs[38] = dsaf_read_dev(drv, GMAC_RX_RUNT_ERR_CNT_REG); + regs[39] = dsaf_read_dev(drv, GMAC_RX_SHORT_ERR_CNT_REG); + regs[40] = dsaf_read_dev(drv, GMAC_RX_FILT_PKT_CNT_REG); + regs[41] = dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_FILT_REG); + + /* tx static registers */ + regs[42] = dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_OK_REG); + regs[43] = dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_BAD_REG); + regs[44] = dsaf_read_dev(drv, GMAC_TX_UC_PKTS_REG); + regs[45] = dsaf_read_dev(drv, GMAC_TX_MC_PKTS_REG); + regs[46] = dsaf_read_dev(drv, GMAC_TX_BC_PKTS_REG); + regs[47] = dsaf_read_dev(drv, GMAC_TX_PKTS_64OCTETS_REG); + regs[48] = dsaf_read_dev(drv, GMAC_TX_PKTS_65TO127OCTETS_REG); + regs[49] = dsaf_read_dev(drv, GMAC_TX_PKTS_128TO255OCTETS_REG); + regs[50] = dsaf_read_dev(drv, GMAC_TX_PKTS_255TO511OCTETS_REG); + regs[51] = dsaf_read_dev(drv, GMAC_TX_PKTS_512TO1023OCTETS_REG); + regs[52] = dsaf_read_dev(drv, GMAC_TX_PKTS_1024TO1518OCTETS_REG); + regs[53] = dsaf_read_dev(drv, GMAC_TX_PKTS_1519TOMAXOCTETS_REG); + regs[54] = dsaf_read_dev(drv, GMAC_TX_EXCESSIVE_LENGTH_DROP_REG); + regs[55] = dsaf_read_dev(drv, GMAC_TX_UNDERRUN_REG); + regs[56] = dsaf_read_dev(drv, GMAC_TX_TAGGED_REG); + regs[57] = dsaf_read_dev(drv, GMAC_TX_CRC_ERROR_REG); + regs[58] = dsaf_read_dev(drv, GMAC_TX_PAUSE_FRAMES_REG); + + regs[59] = dsaf_read_dev(drv, GAMC_RX_MAX_FRAME); + regs[60] = dsaf_read_dev(drv, GMAC_LINE_LOOP_BACK_REG); + regs[61] = dsaf_read_dev(drv, GMAC_CF_CRC_STRIP_REG); + regs[62] = dsaf_read_dev(drv, GMAC_MODE_CHANGE_EN_REG); + regs[63] = dsaf_read_dev(drv, GMAC_SIXTEEN_BIT_CNTR_REG); + regs[64] = dsaf_read_dev(drv, GMAC_LD_LINK_COUNTER_REG); + regs[65] = dsaf_read_dev(drv, GMAC_LOOP_REG); + regs[66] = dsaf_read_dev(drv, GMAC_RECV_CONTROL_REG); + regs[67] = dsaf_read_dev(drv, GMAC_VLAN_CODE_REG); + regs[68] = dsaf_read_dev(drv, GMAC_RX_OVERRUN_CNT_REG); + regs[69] = dsaf_read_dev(drv, GMAC_RX_LENGTHFIELD_ERR_CNT_REG); + regs[70] = dsaf_read_dev(drv, GMAC_RX_FAIL_COMMA_CNT_REG); + + regs[71] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_0_REG); + regs[72] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_0_REG); + regs[73] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_1_REG); + regs[74] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_1_REG); + regs[75] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_2_REG); + regs[76] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_2_REG); + regs[77] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_3_REG); + regs[78] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_3_REG); + regs[79] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_4_REG); + regs[80] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_4_REG); + regs[81] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_5_REG); + regs[82] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_5_REG); + regs[83] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_MSK_0_REG); + regs[84] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_MSK_0_REG); + regs[85] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_MSK_1_REG); + regs[86] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_MSK_1_REG); + regs[87] = dsaf_read_dev(drv, GMAC_MAC_SKIP_LEN_REG); + regs[88] = dsaf_read_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG); + + /* mark end of mac regs */ + for (i = 89; i < 96; i++) + regs[i] = 0xaaaaaaaa; +} + +static void hns_gmac_get_stats(void *mac_drv, u64 *data) +{ + u32 i; + u64 *buf = data; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct mac_hw_stats *hw_stats = NULL; + + hw_stats = &drv->mac_cb->hw_stats; + + for (i = 0; i < ARRAY_SIZE(g_gmac_stats_string); i++) { + buf[i] = DSAF_STATS_READ(hw_stats, + g_gmac_stats_string[i].offset); + } +} + +static void hns_gmac_get_strings(u32 stringset, u8 *data) +{ + char *buff = (char *)data; + u32 i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(g_gmac_stats_string); i++) { + snprintf(buff, ETH_GSTRING_LEN, g_gmac_stats_string[i].desc); + buff = buff + ETH_GSTRING_LEN; + } +} + +static int hns_gmac_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return ARRAY_SIZE(g_gmac_stats_string); + + return 0; +} + +static int hns_gmac_get_regs_count(void) +{ + return ETH_GMAC_DUMP_NUM; +} + +void *hns_gmac_config(struct hns_mac_cb *mac_cb, struct mac_params *mac_param) +{ + struct mac_driver *mac_drv; + + mac_drv = devm_kzalloc(mac_cb->dev, sizeof(*mac_drv), GFP_KERNEL); + if (!mac_drv) + return NULL; + + mac_drv->mac_init = hns_gmac_init; + mac_drv->mac_enable = hns_gmac_enable; + mac_drv->mac_disable = hns_gmac_disable; + mac_drv->mac_free = hns_gmac_free; + mac_drv->adjust_link = hns_gmac_adjust_link; + mac_drv->set_tx_auto_pause_frames = hns_gmac_set_tx_auto_pause_frames; + mac_drv->config_max_frame_length = hns_gmac_config_max_frame_length; + mac_drv->mac_pausefrm_cfg = hns_gmac_pause_frm_cfg; + + mac_drv->mac_id = mac_param->mac_id; + mac_drv->mac_mode = mac_param->mac_mode; + mac_drv->io_base = mac_param->vaddr; + mac_drv->dev = mac_param->dev; + mac_drv->mac_cb = mac_cb; + + mac_drv->set_mac_addr = hns_gmac_set_mac_addr; + mac_drv->set_an_mode = hns_gmac_config_an_mode; + mac_drv->config_loopback = hns_gmac_config_loopback; + mac_drv->config_pad_and_crc = hns_gmac_config_pad_and_crc; + mac_drv->config_half_duplex = hns_gmac_set_duplex_type; + mac_drv->set_rx_ignore_pause_frames = hns_gmac_set_rx_auto_pause_frames; + mac_drv->mac_get_id = hns_gmac_get_id; + mac_drv->get_info = hns_gmac_get_info; + mac_drv->autoneg_stat = hns_gmac_autoneg_stat; + mac_drv->get_pause_enable = hns_gmac_get_pausefrm_cfg; + mac_drv->get_link_status = hns_gmac_get_link_status; + mac_drv->get_regs = hns_gmac_get_regs; + mac_drv->get_regs_count = hns_gmac_get_regs_count; + mac_drv->get_ethtool_stats = hns_gmac_get_stats; + mac_drv->get_sset_count = hns_gmac_get_sset_count; + mac_drv->get_strings = hns_gmac_get_strings; + mac_drv->update_stats = hns_gmac_update_stats; + + return (void *)mac_drv; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h new file mode 100644 index 000000000000..44fe3010dc6d --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_GMAC_H +#define _HNS_GMAC_H + +#include "hns_dsaf_mac.h" + +enum hns_port_mode { + GMAC_10M_MII = 0, + GMAC_100M_MII, + GMAC_1000M_GMII, + GMAC_10M_RGMII, + GMAC_100M_RGMII, + GMAC_1000M_RGMII, + GMAC_10M_SGMII, + GMAC_100M_SGMII, + GMAC_1000M_SGMII, + GMAC_10000M_SGMII /* 10GE */ +}; + +enum hns_gmac_duplex_mdoe { + GMAC_HALF_DUPLEX_MODE = 0, + GMAC_FULL_DUPLEX_MODE +}; + +struct hns_gmac_port_mode_cfg { + enum hns_port_mode port_mode; + u32 max_frm_size; + u32 short_runts_thr; + u32 pad_enable; + u32 crc_add; + u32 an_enable; /*auto-nego enable */ + u32 runt_pkt_en; + u32 strip_pad_en; +}; + +#define ETH_GMAC_DUMP_NUM 96 +#endif /* __HNS_GMAC_H__ */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c new file mode 100644 index 000000000000..95bf42aae24c --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -0,0 +1,900 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/phy_fixed.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "hns_dsaf_misc.h" +#include "hns_dsaf_main.h" +#include "hns_dsaf_rcb.h" + +#define MAC_EN_FLAG_V 0xada0328 + +static const u16 mac_phy_to_speed[] = { + [PHY_INTERFACE_MODE_MII] = MAC_SPEED_100, + [PHY_INTERFACE_MODE_GMII] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_SGMII] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_TBI] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RMII] = MAC_SPEED_100, + [PHY_INTERFACE_MODE_RGMII] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_ID] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_RXID] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_TXID] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RTBI] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_XGMII] = MAC_SPEED_10000 +}; + +static const enum mac_mode g_mac_mode_100[] = { + [PHY_INTERFACE_MODE_MII] = MAC_MODE_MII_100, + [PHY_INTERFACE_MODE_RMII] = MAC_MODE_RMII_100 +}; + +static const enum mac_mode g_mac_mode_1000[] = { + [PHY_INTERFACE_MODE_GMII] = MAC_MODE_GMII_1000, + [PHY_INTERFACE_MODE_SGMII] = MAC_MODE_SGMII_1000, + [PHY_INTERFACE_MODE_TBI] = MAC_MODE_TBI_1000, + [PHY_INTERFACE_MODE_RGMII] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RGMII_ID] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RGMII_RXID] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RGMII_TXID] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RTBI] = MAC_MODE_RTBI_1000 +}; + +static enum mac_mode hns_mac_dev_to_enet_if(const struct hns_mac_cb *mac_cb) +{ + switch (mac_cb->max_speed) { + case MAC_SPEED_100: + return g_mac_mode_100[mac_cb->phy_if]; + case MAC_SPEED_1000: + return g_mac_mode_1000[mac_cb->phy_if]; + case MAC_SPEED_10000: + return MAC_MODE_XGMII_10000; + default: + return MAC_MODE_MII_100; + } +} + +static enum mac_mode hns_get_enet_interface(const struct hns_mac_cb *mac_cb) +{ + switch (mac_cb->max_speed) { + case MAC_SPEED_100: + return g_mac_mode_100[mac_cb->phy_if]; + case MAC_SPEED_1000: + return g_mac_mode_1000[mac_cb->phy_if]; + case MAC_SPEED_10000: + return MAC_MODE_XGMII_10000; + default: + return MAC_MODE_MII_100; + } +} + +int hns_mac_get_sfp_prsnt(struct hns_mac_cb *mac_cb, int *sfp_prsnt) +{ + if (!mac_cb->cpld_vaddr) + return -ENODEV; + + *sfp_prsnt = !dsaf_read_b((u8 *)mac_cb->cpld_vaddr + + MAC_SFP_PORT_OFFSET); + + return 0; +} + +void hns_mac_get_link_status(struct hns_mac_cb *mac_cb, u32 *link_status) +{ + struct mac_driver *mac_ctrl_drv; + int ret, sfp_prsnt; + + mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_ctrl_drv->get_link_status) + mac_ctrl_drv->get_link_status(mac_ctrl_drv, link_status); + else + *link_status = 0; + + ret = hns_mac_get_sfp_prsnt(mac_cb, &sfp_prsnt); + if (!ret) + *link_status = *link_status && sfp_prsnt; + + mac_cb->link = *link_status; +} + +int hns_mac_get_port_info(struct hns_mac_cb *mac_cb, + u8 *auto_neg, u16 *speed, u8 *duplex) +{ + struct mac_driver *mac_ctrl_drv; + struct mac_info info; + + mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (!mac_ctrl_drv->get_info) + return -ENODEV; + + mac_ctrl_drv->get_info(mac_ctrl_drv, &info); + if (auto_neg) + *auto_neg = info.auto_neg; + if (speed) + *speed = info.speed; + if (duplex) + *duplex = info.duplex; + + return 0; +} + +void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex) +{ + int ret; + struct mac_driver *mac_ctrl_drv; + + mac_ctrl_drv = (struct mac_driver *)(mac_cb->priv.mac); + + mac_cb->speed = speed; + mac_cb->half_duplex = !duplex; + mac_ctrl_drv->mac_mode = hns_mac_dev_to_enet_if(mac_cb); + + if (mac_ctrl_drv->adjust_link) { + ret = mac_ctrl_drv->adjust_link(mac_ctrl_drv, + (enum mac_speed)speed, duplex); + if (ret) { + dev_err(mac_cb->dev, + "adjust_link failed,%s mac%d ret = %#x!\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, ret); + return; + } + } +} + +/** + *hns_mac_get_inner_port_num - get mac table inner port number + *@mac_cb: mac device + *@vmid: vm id + *@port_num:port number + * + */ +static int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb, + u8 vmid, u8 *port_num) +{ + u8 tmp_port; + u32 comm_idx; + + if (mac_cb->dsaf_dev->dsaf_mode <= DSAF_MODE_ENABLE) { + if (mac_cb->mac_id != DSAF_MAX_PORT_NUM_PER_CHIP) { + dev_err(mac_cb->dev, + "input invalid,%s mac%d vmid%d !\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, vmid); + return -EINVAL; + } + } else if (mac_cb->dsaf_dev->dsaf_mode < DSAF_MODE_MAX) { + if (mac_cb->mac_id <= DSAF_MAX_PORT_NUM_PER_CHIP) { + dev_err(mac_cb->dev, + "input invalid,%s mac%d vmid%d!\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, vmid); + return -EINVAL; + } + } else { + dev_err(mac_cb->dev, "dsaf mode invalid,%s mac%d!\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id); + return -EINVAL; + } + + comm_idx = hns_dsaf_get_comm_idx_by_port(mac_cb->mac_id); + + if (vmid >= mac_cb->dsaf_dev->rcb_common[comm_idx]->max_vfn) { + dev_err(mac_cb->dev, "input invalid,%s mac%d vmid%d !\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id, vmid); + return -EINVAL; + } + + switch (mac_cb->dsaf_dev->dsaf_mode) { + case DSAF_MODE_ENABLE_FIX: + tmp_port = 0; + break; + case DSAF_MODE_DISABLE_FIX: + tmp_port = 0; + break; + case DSAF_MODE_ENABLE_0VM: + case DSAF_MODE_ENABLE_8VM: + case DSAF_MODE_ENABLE_16VM: + case DSAF_MODE_ENABLE_32VM: + case DSAF_MODE_ENABLE_128VM: + case DSAF_MODE_DISABLE_2PORT_8VM: + case DSAF_MODE_DISABLE_2PORT_16VM: + case DSAF_MODE_DISABLE_2PORT_64VM: + case DSAF_MODE_DISABLE_6PORT_0VM: + case DSAF_MODE_DISABLE_6PORT_2VM: + case DSAF_MODE_DISABLE_6PORT_4VM: + case DSAF_MODE_DISABLE_6PORT_16VM: + tmp_port = vmid; + break; + default: + dev_err(mac_cb->dev, "dsaf mode invalid,%s mac%d!\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id); + return -EINVAL; + } + tmp_port += DSAF_BASE_INNER_PORT_NUM; + + *port_num = tmp_port; + + return 0; +} + +/** + *hns_mac_get_inner_port_num - change vf mac address + *@mac_cb: mac device + *@vmid: vmid + *@addr:mac address + */ +int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, + u32 vmid, char *addr) +{ + int ret; + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + struct dsaf_drv_mac_single_dest_entry mac_entry; + struct mac_entry_idx *old_entry; + + old_entry = &mac_cb->addr_entry_idx[vmid]; + if (dsaf_dev) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = old_entry->vlan_id; + mac_entry.in_port_num = mac_cb->mac_id; + ret = hns_mac_get_inner_port_num(mac_cb, (u8)vmid, + &mac_entry.port_num); + if (ret) + return ret; + + if ((old_entry->valid != 0) && + (memcmp(old_entry->addr, + addr, sizeof(mac_entry.addr)) != 0)) { + ret = hns_dsaf_del_mac_entry(dsaf_dev, + old_entry->vlan_id, + mac_cb->mac_id, + old_entry->addr); + if (ret) + return ret; + } + + ret = hns_dsaf_set_mac_uc_entry(dsaf_dev, &mac_entry); + if (ret) + return ret; + } + + if ((mac_ctrl_drv->set_mac_addr) && (vmid == 0)) + mac_ctrl_drv->set_mac_addr(mac_cb->priv.mac, addr); + + memcpy(old_entry->addr, addr, sizeof(old_entry->addr)); + old_entry->valid = 1; + return 0; +} + +int hns_mac_set_multi(struct hns_mac_cb *mac_cb, + u32 port_num, char *addr, u8 en) +{ + int ret; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + struct dsaf_drv_mac_single_dest_entry mac_entry; + + if (dsaf_dev && addr) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = 0;/*vlan_id;*/ + mac_entry.in_port_num = mac_cb->mac_id; + mac_entry.port_num = port_num; + + if (en == DISABLE) + ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); + else + ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); + if (ret) { + dev_err(dsaf_dev->dev, + "set mac mc port failed,%s mac%d ret = %#x!\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, ret); + return ret; + } + } + + return 0; +} + +/** + *hns_mac_del_mac - delete mac address into dsaf table,can't delete the same + * address twice + *@net_dev: net device + *@vfn : vf lan + *@mac : mac address + *return status + */ +int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac) +{ + struct mac_entry_idx *old_mac; + struct dsaf_device *dsaf_dev; + u32 ret; + + dsaf_dev = mac_cb->dsaf_dev; + + if (vfn < DSAF_MAX_VM_NUM) { + old_mac = &mac_cb->addr_entry_idx[vfn]; + } else { + dev_err(mac_cb->dev, + "vf queue is too large,%s mac%d queue = %#x!\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id, vfn); + return -EINVAL; + } + + if (dsaf_dev) { + ret = hns_dsaf_del_mac_entry(dsaf_dev, old_mac->vlan_id, + mac_cb->mac_id, old_mac->addr); + if (ret) + return ret; + + if (memcmp(old_mac->addr, mac, sizeof(old_mac->addr)) == 0) + old_mac->valid = 0; + } + + return 0; +} + +static void hns_mac_param_get(struct mac_params *param, + struct hns_mac_cb *mac_cb) +{ + param->vaddr = (void *)mac_cb->vaddr; + param->mac_mode = hns_get_enet_interface(mac_cb); + memcpy(param->addr, mac_cb->addr_entry_idx[0].addr, + MAC_NUM_OCTETS_PER_ADDR); + param->mac_id = mac_cb->mac_id; + param->dev = mac_cb->dev; +} + +/** + *hns_mac_queue_config_bc_en - set broadcast rx&tx enable + *@mac_cb: mac device + *@queue: queue number + *@en:enable + *retuen 0 - success , negative --fail + */ +static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb, + u32 port_num, u16 vlan_id, u8 en) +{ + int ret; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + u8 addr[MAC_NUM_OCTETS_PER_ADDR] + = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + struct dsaf_drv_mac_single_dest_entry mac_entry; + + /* directy return ok in debug network mode */ + if (mac_cb->mac_type == HNAE_PORT_DEBUG) + return 0; + + if (dsaf_dev) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = vlan_id; + mac_entry.in_port_num = mac_cb->mac_id; + mac_entry.port_num = port_num; + + if (en == DISABLE) + ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); + else + ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); + return ret; + } + + return 0; +} + +/** + *hns_mac_vm_config_bc_en - set broadcast rx&tx enable + *@mac_cb: mac device + *@vmid: vm id + *@en:enable + *retuen 0 - success , negative --fail + */ +int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, u8 en) +{ + int ret; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + u8 port_num; + u8 addr[MAC_NUM_OCTETS_PER_ADDR] + = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + struct mac_entry_idx *uc_mac_entry; + struct dsaf_drv_mac_single_dest_entry mac_entry; + + if (mac_cb->mac_type == HNAE_PORT_DEBUG) + return 0; + + uc_mac_entry = &mac_cb->addr_entry_idx[vmid]; + + if (dsaf_dev) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = uc_mac_entry->vlan_id; + mac_entry.in_port_num = mac_cb->mac_id; + ret = hns_mac_get_inner_port_num(mac_cb, vmid, &port_num); + if (ret) + return ret; + mac_entry.port_num = port_num; + + if (en == DISABLE) + ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); + else + ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); + return ret; + } + + return 0; +} + +void hns_mac_reset(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *drv; + + drv = hns_mac_get_drv(mac_cb); + + drv->mac_init(drv); + + if (drv->config_max_frame_length) + drv->config_max_frame_length(drv, mac_cb->max_frm); + + if (drv->set_tx_auto_pause_frames) + drv->set_tx_auto_pause_frames(drv, mac_cb->tx_pause_frm_time); + + if (drv->set_an_mode) + drv->set_an_mode(drv, 1); + + if (drv->mac_pausefrm_cfg) { + if (mac_cb->mac_type == HNAE_PORT_DEBUG) + drv->mac_pausefrm_cfg(drv, 0, 0); + else /* mac rx must disable, dsaf pfc close instead of it*/ + drv->mac_pausefrm_cfg(drv, 0, 1); + } +} + +int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu) +{ + struct mac_driver *drv = hns_mac_get_drv(mac_cb); + u32 buf_size = mac_cb->dsaf_dev->buf_size; + u32 new_frm = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + + if ((new_mtu < MAC_MIN_MTU) || (new_frm > MAC_MAX_MTU) || + (new_frm > HNS_RCB_RING_MAX_BD_PER_PKT * buf_size)) + return -EINVAL; + + if (!drv->config_max_frame_length) + return -ECHILD; + + /* adjust max frame to be at least the size of a standard frame */ + if (new_frm < (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN)) + new_frm = (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN); + + drv->config_max_frame_length(drv, new_frm); + + mac_cb->max_frm = new_frm; + + return 0; +} + +void hns_mac_start(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_drv = hns_mac_get_drv(mac_cb); + + /* for virt */ + if (mac_drv->mac_en_flg == MAC_EN_FLAG_V) { + /*plus 1 when the virtual mac has been enabled */ + mac_drv->virt_dev_num += 1; + return; + } + + if (mac_drv->mac_enable) { + mac_drv->mac_enable(mac_cb->priv.mac, MAC_COMM_MODE_RX_AND_TX); + mac_drv->mac_en_flg = MAC_EN_FLAG_V; + } +} + +void hns_mac_stop(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + /*modified for virtualization */ + if (mac_ctrl_drv->virt_dev_num > 0) { + mac_ctrl_drv->virt_dev_num -= 1; + if (mac_ctrl_drv->virt_dev_num > 0) + return; + } + + if (mac_ctrl_drv->mac_disable) + mac_ctrl_drv->mac_disable(mac_cb->priv.mac, + MAC_COMM_MODE_RX_AND_TX); + + mac_ctrl_drv->mac_en_flg = 0; + mac_cb->link = 0; + cpld_led_reset(mac_cb); +} + +/** + * hns_mac_get_autoneg - get auto autonegotiation + * @mac_cb: mac control block + * @enable: enable or not + * retuen 0 - success , negative --fail + */ +void hns_mac_get_autoneg(struct hns_mac_cb *mac_cb, u32 *auto_neg) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_ctrl_drv->autoneg_stat) + mac_ctrl_drv->autoneg_stat(mac_ctrl_drv, auto_neg); + else + *auto_neg = 0; +} + +/** + * hns_mac_get_pauseparam - set rx & tx pause parameter + * @mac_cb: mac control block + * @rx_en: rx enable status + * @tx_en: tx enable status + * retuen 0 - success , negative --fail + */ +void hns_mac_get_pauseparam(struct hns_mac_cb *mac_cb, u32 *rx_en, u32 *tx_en) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_ctrl_drv->get_pause_enable) { + mac_ctrl_drv->get_pause_enable(mac_ctrl_drv, rx_en, tx_en); + } else { + *rx_en = 0; + *tx_en = 0; + } + + /* Due to the chip defect, the service mac's rx pause CAN'T be enabled. + * We set the rx pause frm always be true (1), because DSAF deals with + * the rx pause frm instead of service mac. After all, we still support + * rx pause frm. + */ + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + *rx_en = 1; +} + +/** + * hns_mac_set_autoneg - set auto autonegotiation + * @mac_cb: mac control block + * @enable: enable or not + * retuen 0 - success , negative --fail + */ +int hns_mac_set_autoneg(struct hns_mac_cb *mac_cb, u8 enable) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII && enable) { + dev_err(mac_cb->dev, "enable autoneg is not allowed!"); + return -ENOTSUPP; + } + + if (mac_ctrl_drv->set_an_mode) + mac_ctrl_drv->set_an_mode(mac_ctrl_drv, enable); + + return 0; +} + +/** + * hns_mac_set_autoneg - set rx & tx pause parameter + * @mac_cb: mac control block + * @rx_en: rx enable or not + * @tx_en: tx enable or not + * return 0 - success , negative --fail + */ +int hns_mac_set_pauseparam(struct hns_mac_cb *mac_cb, u32 rx_en, u32 tx_en) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) { + if (!rx_en) { + dev_err(mac_cb->dev, "disable rx_pause is not allowed!"); + return -EINVAL; + } + } else if (mac_cb->mac_type == HNAE_PORT_DEBUG) { + if (tx_en || rx_en) { + dev_err(mac_cb->dev, "enable tx_pause or enable rx_pause are not allowed!"); + return -EINVAL; + } + } else { + dev_err(mac_cb->dev, "Unsupport this operation!"); + return -EINVAL; + } + + if (mac_ctrl_drv->mac_pausefrm_cfg) + mac_ctrl_drv->mac_pausefrm_cfg(mac_ctrl_drv, rx_en, tx_en); + + return 0; +} + +/** + * hns_mac_init_ex - mac init + * @mac_cb: mac control block + * retuen 0 - success , negative --fail + */ +static int hns_mac_init_ex(struct hns_mac_cb *mac_cb) +{ + int ret; + struct mac_params param; + struct mac_driver *drv; + + hns_dsaf_fix_mac_mode(mac_cb); + + memset(¶m, 0, sizeof(struct mac_params)); + hns_mac_param_get(¶m, mac_cb); + + if (MAC_SPEED_FROM_MODE(param.mac_mode) < MAC_SPEED_10000) + drv = (struct mac_driver *)hns_gmac_config(mac_cb, ¶m); + else + drv = (struct mac_driver *)hns_xgmac_config(mac_cb, ¶m); + + if (!drv) + return -ENOMEM; + + mac_cb->priv.mac = (void *)drv; + hns_mac_reset(mac_cb); + + hns_mac_adjust_link(mac_cb, mac_cb->speed, !mac_cb->half_duplex); + + ret = hns_mac_port_config_bc_en(mac_cb, mac_cb->mac_id, 0, ENABLE); + if (ret) + goto free_mac_drv; + + return 0; + +free_mac_drv: + drv->mac_free(mac_cb->priv.mac); + mac_cb->priv.mac = NULL; + + return ret; +} + +/** + *mac_free_dev - get mac information from device node + *@mac_cb: mac device + *@np:device node + *@mac_mode_idx:mac mode index + */ +static void hns_mac_get_info(struct hns_mac_cb *mac_cb, + struct device_node *np, u32 mac_mode_idx) +{ + mac_cb->link = false; + mac_cb->half_duplex = false; + mac_cb->speed = mac_phy_to_speed[mac_cb->phy_if]; + mac_cb->max_speed = mac_cb->speed; + + if (mac_cb->phy_if == PHY_INTERFACE_MODE_SGMII) { + mac_cb->if_support = MAC_GMAC_SUPPORTED; + mac_cb->if_support |= SUPPORTED_1000baseT_Full; + } else if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII) { + mac_cb->if_support = SUPPORTED_10000baseR_FEC; + mac_cb->if_support |= SUPPORTED_10000baseKR_Full; + } + + mac_cb->max_frm = MAC_DEFAULT_MTU; + mac_cb->tx_pause_frm_time = MAC_DEFAULT_PAUSE_TIME; + + /* Get the rest of the PHY information */ + mac_cb->phy_node = of_parse_phandle(np, "phy-handle", mac_cb->mac_id); + if (mac_cb->phy_node) + dev_dbg(mac_cb->dev, "mac%d phy_node: %s\n", + mac_cb->mac_id, mac_cb->phy_node->name); +} + +/** + * hns_mac_get_mode - get mac mode + * @phy_if: phy interface + * retuen 0 - gmac, 1 - xgmac , negative --fail + */ +static int hns_mac_get_mode(phy_interface_t phy_if) +{ + switch (phy_if) { + case PHY_INTERFACE_MODE_SGMII: + return MAC_GMAC_IDX; + case PHY_INTERFACE_MODE_XGMII: + return MAC_XGMAC_IDX; + default: + return -EINVAL; + } +} + +u8 __iomem *hns_mac_get_vaddr(struct dsaf_device *dsaf_dev, + struct hns_mac_cb *mac_cb, u32 mac_mode_idx) +{ + u8 __iomem *base = dsaf_dev->io_base; + int mac_id = mac_cb->mac_id; + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + return base + 0x40000 + mac_id * 0x4000 - + mac_mode_idx * 0x20000; + else + return mac_cb->serdes_vaddr + 0x1000 + + (mac_id - DSAF_SERVICE_PORT_NUM_PER_DSAF) * 0x100000; +} + +/** + * hns_mac_get_cfg - get mac cfg from dtb or acpi table + * @dsaf_dev: dsa fabric device struct pointer + * @mac_idx: mac index + * retuen 0 - success , negative --fail + */ +int hns_mac_get_cfg(struct dsaf_device *dsaf_dev, int mac_idx) +{ + int ret; + u32 mac_mode_idx; + struct hns_mac_cb *mac_cb = &dsaf_dev->mac_cb[mac_idx]; + + mac_cb->dsaf_dev = dsaf_dev; + mac_cb->dev = dsaf_dev->dev; + mac_cb->mac_id = mac_idx; + + mac_cb->sys_ctl_vaddr = dsaf_dev->sc_base; + mac_cb->serdes_vaddr = dsaf_dev->sds_base; + + if (dsaf_dev->cpld_base && + mac_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) + mac_cb->cpld_vaddr = dsaf_dev->cpld_base + + mac_cb->mac_id * CPLD_ADDR_PORT_OFFSET; + mac_cb->sfp_prsnt = 0; + mac_cb->txpkt_for_led = 0; + mac_cb->rxpkt_for_led = 0; + + if (mac_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) + mac_cb->mac_type = HNAE_PORT_SERVICE; + else + mac_cb->mac_type = HNAE_PORT_DEBUG; + + mac_cb->phy_if = hns_mac_get_phy_if(mac_cb); + + ret = hns_mac_get_mode(mac_cb->phy_if); + if (ret < 0) { + dev_err(dsaf_dev->dev, + "hns_mac_get_mode failed,mac%d ret = %#x!\n", + mac_cb->mac_id, ret); + return ret; + } + mac_mode_idx = (u32)ret; + + hns_mac_get_info(mac_cb, mac_cb->dev->of_node, mac_mode_idx); + + mac_cb->vaddr = hns_mac_get_vaddr(dsaf_dev, mac_cb, mac_mode_idx); + + return 0; +} + +/** + * hns_mac_init - init mac + * @dsaf_dev: dsa fabric device struct pointer + * retuen 0 - success , negative --fail + */ +int hns_mac_init(struct dsaf_device *dsaf_dev) +{ + int i; + int ret; + size_t size; + struct hns_mac_cb *mac_cb; + + size = sizeof(struct hns_mac_cb) * DSAF_MAX_PORT_NUM_PER_CHIP; + dsaf_dev->mac_cb = devm_kzalloc(dsaf_dev->dev, size, GFP_KERNEL); + if (!dsaf_dev->mac_cb) + return -ENOMEM; + + for (i = 0; i < DSAF_MAX_PORT_NUM_PER_CHIP; i++) { + ret = hns_mac_get_cfg(dsaf_dev, i); + if (ret) + goto free_mac_cb; + + mac_cb = &dsaf_dev->mac_cb[i]; + ret = hns_mac_init_ex(mac_cb); + if (ret) + goto free_mac_cb; + } + + return 0; + +free_mac_cb: + dsaf_dev->mac_cb = NULL; + + return ret; +} + +void hns_mac_uninit(struct dsaf_device *dsaf_dev) +{ + cpld_led_reset(dsaf_dev->mac_cb); + dsaf_dev->mac_cb = NULL; +} + +int hns_mac_config_mac_loopback(struct hns_mac_cb *mac_cb, + enum hnae_loop loop, int en) +{ + int ret; + struct mac_driver *drv = hns_mac_get_drv(mac_cb); + + if (drv->config_loopback) + ret = drv->config_loopback(drv, loop, en); + else + ret = -ENOTSUPP; + + return ret; +} + +void hns_mac_update_stats(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->update_stats(mac_ctrl_drv); +} + +void hns_mac_get_stats(struct hns_mac_cb *mac_cb, u64 *data) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->get_ethtool_stats(mac_ctrl_drv, data); +} + +void hns_mac_get_strings(struct hns_mac_cb *mac_cb, + int stringset, u8 *data) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->get_strings(stringset, data); +} + +int hns_mac_get_sset_count(struct hns_mac_cb *mac_cb, int stringset) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + return mac_ctrl_drv->get_sset_count(stringset); +} + +int hns_mac_get_regs_count(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + return mac_ctrl_drv->get_regs_count(); +} + +void hns_mac_get_regs(struct hns_mac_cb *mac_cb, void *data) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->get_regs(mac_ctrl_drv, data); +} + +void hns_set_led_opt(struct hns_mac_cb *mac_cb) +{ + int nic_data = 0; + int txpkts, rxpkts; + + txpkts = mac_cb->txpkt_for_led - mac_cb->hw_stats.tx_good_pkts; + rxpkts = mac_cb->rxpkt_for_led - mac_cb->hw_stats.rx_good_pkts; + if (txpkts || rxpkts) + nic_data = 1; + else + nic_data = 0; + mac_cb->txpkt_for_led = mac_cb->hw_stats.tx_good_pkts; + mac_cb->rxpkt_for_led = mac_cb->hw_stats.rx_good_pkts; + hns_cpld_set_led(mac_cb, (int)mac_cb->link, + mac_cb->speed, nic_data); +} + +int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status) +{ + if (!mac_cb || !mac_cb->cpld_vaddr) + return 0; + + return cpld_set_led_id(mac_cb, status); +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h new file mode 100644 index 000000000000..7da95a7581f9 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_DSAF_MAC_H +#define _HNS_DSAF_MAC_H + +#include <linux/phy.h> +#include <linux/kernel.h> +#include <linux/if_vlan.h> +#include "hns_dsaf_main.h" + +struct dsaf_device; + +#define MAC_GMAC_SUPPORTED \ + (SUPPORTED_10baseT_Half \ + | SUPPORTED_10baseT_Full \ + | SUPPORTED_100baseT_Half \ + | SUPPORTED_100baseT_Full \ + | SUPPORTED_Autoneg) + +#define MAC_DEFAULT_MTU (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN + ETH_DATA_LEN) +#define MAC_MAX_MTU 9600 +#define MAC_MIN_MTU 68 + +#define MAC_DEFAULT_PAUSE_TIME 0xff + +#define MAC_GMAC_IDX 0 +#define MAC_XGMAC_IDX 1 + +#define ETH_STATIC_REG 1 +#define ETH_DUMP_REG 5 +/* check mac addr broadcast */ +#define MAC_IS_BROADCAST(p) ((*(p) == 0xff) && (*((p) + 1) == 0xff) && \ + (*((p) + 2) == 0xff) && (*((p) + 3) == 0xff) && \ + (*((p) + 4) == 0xff) && (*((p) + 5) == 0xff)) + +/* check mac addr is 01-00-5e-xx-xx-xx*/ +#define MAC_IS_L3_MULTICAST(p) ((*((p) + 0) == 0x01) && \ + (*((p) + 1) == 0x00) && \ + (*((p) + 2) == 0x5e)) + +/*check the mac addr is 0 in all bit*/ +#define MAC_IS_ALL_ZEROS(p) ((*(p) == 0) && (*((p) + 1) == 0) && \ + (*((p) + 2) == 0) && (*((p) + 3) == 0) && \ + (*((p) + 4) == 0) && (*((p) + 5) == 0)) + +/*check mac addr multicast*/ +#define MAC_IS_MULTICAST(p) ((*((u8 *)((p) + 0)) & 0x01) ? (1) : (0)) + +/**< Number of octets (8-bit bytes) in an ethernet address */ +#define MAC_NUM_OCTETS_PER_ADDR 6 + +struct mac_priv { + void *mac; +}; + +/* net speed */ +enum mac_speed { + MAC_SPEED_10 = 10, /**< 10 Mbps */ + MAC_SPEED_100 = 100, /**< 100 Mbps */ + MAC_SPEED_1000 = 1000, /**< 1000 Mbps = 1 Gbps */ + MAC_SPEED_10000 = 10000 /**< 10000 Mbps = 10 Gbps */ +}; + +/*mac interface keyword */ +enum mac_intf { + MAC_IF_NONE = 0x00000000, /**< interface not invalid */ + MAC_IF_MII = 0x00010000, /**< MII interface */ + MAC_IF_RMII = 0x00020000, /**< RMII interface */ + MAC_IF_SMII = 0x00030000, /**< SMII interface */ + MAC_IF_GMII = 0x00040000, /**< GMII interface */ + MAC_IF_RGMII = 0x00050000, /**< RGMII interface */ + MAC_IF_TBI = 0x00060000, /**< TBI interface */ + MAC_IF_RTBI = 0x00070000, /**< RTBI interface */ + MAC_IF_SGMII = 0x00080000, /**< SGMII interface */ + MAC_IF_XGMII = 0x00090000, /**< XGMII interface */ + MAC_IF_QSGMII = 0x000a0000 /**< QSGMII interface */ +}; + +/*mac mode */ +enum mac_mode { + /**< Invalid Ethernet mode */ + MAC_MODE_INVALID = 0, + /**< 10 Mbps MII */ + MAC_MODE_MII_10 = (MAC_IF_MII | MAC_SPEED_10), + /**< 100 Mbps MII */ + MAC_MODE_MII_100 = (MAC_IF_MII | MAC_SPEED_100), + /**< 10 Mbps RMII */ + MAC_MODE_RMII_10 = (MAC_IF_RMII | MAC_SPEED_10), + /**< 100 Mbps RMII */ + MAC_MODE_RMII_100 = (MAC_IF_RMII | MAC_SPEED_100), + /**< 10 Mbps SMII */ + MAC_MODE_SMII_10 = (MAC_IF_SMII | MAC_SPEED_10), + /**< 100 Mbps SMII */ + MAC_MODE_SMII_100 = (MAC_IF_SMII | MAC_SPEED_100), + /**< 1000 Mbps GMII */ + MAC_MODE_GMII_1000 = (MAC_IF_GMII | MAC_SPEED_1000), + /**< 10 Mbps RGMII */ + MAC_MODE_RGMII_10 = (MAC_IF_RGMII | MAC_SPEED_10), + /**< 100 Mbps RGMII */ + MAC_MODE_RGMII_100 = (MAC_IF_RGMII | MAC_SPEED_100), + /**< 1000 Mbps RGMII */ + MAC_MODE_RGMII_1000 = (MAC_IF_RGMII | MAC_SPEED_1000), + /**< 1000 Mbps TBI */ + MAC_MODE_TBI_1000 = (MAC_IF_TBI | MAC_SPEED_1000), + /**< 1000 Mbps RTBI */ + MAC_MODE_RTBI_1000 = (MAC_IF_RTBI | MAC_SPEED_1000), + /**< 10 Mbps SGMII */ + MAC_MODE_SGMII_10 = (MAC_IF_SGMII | MAC_SPEED_10), + /**< 100 Mbps SGMII */ + MAC_MODE_SGMII_100 = (MAC_IF_SGMII | MAC_SPEED_100), + /**< 1000 Mbps SGMII */ + MAC_MODE_SGMII_1000 = (MAC_IF_SGMII | MAC_SPEED_1000), + /**< 10000 Mbps XGMII */ + MAC_MODE_XGMII_10000 = (MAC_IF_XGMII | MAC_SPEED_10000), + /**< 1000 Mbps QSGMII */ + MAC_MODE_QSGMII_1000 = (MAC_IF_QSGMII | MAC_SPEED_1000) +}; + +/*mac communicate mode*/ +enum mac_commom_mode { + MAC_COMM_MODE_NONE = 0, /**< No transmit/receive communication */ + MAC_COMM_MODE_RX = 1, /**< Only receive communication */ + MAC_COMM_MODE_TX = 2, /**< Only transmit communication */ + MAC_COMM_MODE_RX_AND_TX = 3 /**< Both tx and rx communication */ +}; + +/*mac statistics */ +struct mac_statistics { + u64 stat_pkts64; /* r-10G tr-DT 64 byte frame counter */ + u64 stat_pkts65to127; /* r-10G 65 to 127 byte frame counter */ + u64 stat_pkts128to255; /* r-10G 128 to 255 byte frame counter */ + u64 stat_pkts256to511; /*r-10G 256 to 511 byte frame counter */ + u64 stat_pkts512to1023;/* r-10G 512 to 1023 byte frame counter */ + u64 stat_pkts1024to1518; /* r-10G 1024 to 1518 byte frame counter */ + u64 stat_pkts1519to1522; /* r-10G 1519 to 1522 byte good frame count*/ + /* Total number of packets that were less than 64 octets */ + /* long with a wrong CRC.*/ + u64 stat_fragments; + /* Total number of packets longer than valid maximum length octets */ + u64 stat_jabbers; + /* number of dropped packets due to internal errors of */ + /* the MAC Client. */ + u64 stat_drop_events; + /* Incremented when frames of correct length but with */ + /* CRC error are received.*/ + u64 stat_crc_align_errors; + /* Total number of packets that were less than 64 octets */ + /* long with a good CRC.*/ + u64 stat_undersize_pkts; + u64 stat_oversize_pkts; /**< T,B.D*/ + + u64 stat_rx_pause; /**< Pause MAC Control received */ + u64 stat_tx_pause; /**< Pause MAC Control sent */ + + u64 in_octets; /**< Total number of byte received. */ + u64 in_pkts; /* Total number of packets received.*/ + u64 in_mcast_pkts; /* Total number of multicast frame received */ + u64 in_bcast_pkts; /* Total number of broadcast frame received */ + /* Frames received, but discarded due to */ + /* problems within the MAC RX. */ + u64 in_discards; + u64 in_errors; /* Number of frames received with error: */ + /* - FIFO Overflow Error */ + /* - CRC Error */ + /* - Frame Too Long Error */ + /* - Alignment Error */ + u64 out_octets; /*Total number of byte sent. */ + u64 out_pkts; /**< Total number of packets sent .*/ + u64 out_mcast_pkts; /* Total number of multicast frame sent */ + u64 out_bcast_pkts; /* Total number of multicast frame sent */ + /* Frames received, but discarded due to problems within */ + /* the MAC TX N/A!.*/ + u64 out_discards; + u64 out_errors; /*Number of frames transmitted with error: */ + /* - FIFO Overflow Error */ + /* - FIFO Underflow Error */ + /* - Other */ +}; + +/*mac para struct ,mac get param from nic or dsaf when initialize*/ +struct mac_params { + char addr[MAC_NUM_OCTETS_PER_ADDR]; + void *vaddr; /*virtual address*/ + struct device *dev; + u8 mac_id; + /**< Ethernet operation mode (MAC-PHY interface and speed) */ + enum mac_mode mac_mode; +}; + +struct mac_info { + u16 speed;/* The forced speed (lower bits) in */ + /* *mbps. Please use */ + /* * ethtool_cmd_speed()/_set() to */ + /* * access it */ + u8 duplex; /* Duplex, half or full */ + u8 auto_neg; /* Enable or disable autonegotiation */ + enum hnae_loop loop_mode; + u8 tx_pause_en; + u8 tx_pause_time; + u8 rx_pause_en; + u8 pad_and_crc_en; + u8 promiscuous_en; + u8 port_en; /*port enable*/ +}; + +struct mac_entry_idx { + u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u16 vlan_id:12; + u16 valid:1; + u16 qos:3; +}; + +struct mac_hw_stats { + u64 rx_good_pkts; /* only for xgmac */ + u64 rx_good_bytes; + u64 rx_total_pkts; /* only for xgmac */ + u64 rx_total_bytes; /* only for xgmac */ + u64 rx_bad_bytes; /* only for gmac */ + u64 rx_uc_pkts; + u64 rx_mc_pkts; + u64 rx_bc_pkts; + u64 rx_fragment_err; /* only for xgmac */ + u64 rx_undersize; /* only for xgmac */ + u64 rx_under_min; + u64 rx_minto64; /* only for gmac */ + u64 rx_64bytes; + u64 rx_65to127; + u64 rx_128to255; + u64 rx_256to511; + u64 rx_512to1023; + u64 rx_1024to1518; + u64 rx_1519tomax; + u64 rx_1519tomax_good; /* only for xgmac */ + u64 rx_oversize; + u64 rx_jabber_err; + u64 rx_fcs_err; + u64 rx_vlan_pkts; /* only for gmac */ + u64 rx_data_err; /* only for gmac */ + u64 rx_align_err; /* only for gmac */ + u64 rx_long_err; /* only for gmac */ + u64 rx_pfc_tc0; + u64 rx_pfc_tc1; /* only for xgmac */ + u64 rx_pfc_tc2; /* only for xgmac */ + u64 rx_pfc_tc3; /* only for xgmac */ + u64 rx_pfc_tc4; /* only for xgmac */ + u64 rx_pfc_tc5; /* only for xgmac */ + u64 rx_pfc_tc6; /* only for xgmac */ + u64 rx_pfc_tc7; /* only for xgmac */ + u64 rx_unknown_ctrl; + u64 rx_filter_pkts; /* only for gmac */ + u64 rx_filter_bytes; /* only for gmac */ + u64 rx_fifo_overrun_err;/* only for gmac */ + u64 rx_len_err; /* only for gmac */ + u64 rx_comma_err; /* only for gmac */ + u64 rx_symbol_err; /* only for xgmac */ + u64 tx_good_to_sw; /* only for xgmac */ + u64 tx_bad_to_sw; /* only for xgmac */ + u64 rx_1731_pkts; /* only for xgmac */ + + u64 tx_good_bytes; + u64 tx_good_pkts; /* only for xgmac */ + u64 tx_total_bytes; /* only for xgmac */ + u64 tx_total_pkts; /* only for xgmac */ + u64 tx_bad_bytes; /* only for gmac */ + u64 tx_bad_pkts; /* only for xgmac */ + u64 tx_uc_pkts; + u64 tx_mc_pkts; + u64 tx_bc_pkts; + u64 tx_undersize; /* only for xgmac */ + u64 tx_fragment_err; /* only for xgmac */ + u64 tx_under_min_pkts; /* only for gmac */ + u64 tx_64bytes; + u64 tx_65to127; + u64 tx_128to255; + u64 tx_256to511; + u64 tx_512to1023; + u64 tx_1024to1518; + u64 tx_1519tomax; + u64 tx_1519tomax_good; /* only for xgmac */ + u64 tx_oversize; /* only for xgmac */ + u64 tx_jabber_err; + u64 tx_underrun_err; /* only for gmac */ + u64 tx_vlan; /* only for gmac */ + u64 tx_crc_err; /* only for gmac */ + u64 tx_pfc_tc0; + u64 tx_pfc_tc1; /* only for xgmac */ + u64 tx_pfc_tc2; /* only for xgmac */ + u64 tx_pfc_tc3; /* only for xgmac */ + u64 tx_pfc_tc4; /* only for xgmac */ + u64 tx_pfc_tc5; /* only for xgmac */ + u64 tx_pfc_tc6; /* only for xgmac */ + u64 tx_pfc_tc7; /* only for xgmac */ + u64 tx_ctrl; /* only for xgmac */ + u64 tx_1731_pkts; /* only for xgmac */ + u64 tx_1588_pkts; /* only for xgmac */ + u64 rx_good_from_sw; /* only for xgmac */ + u64 rx_bad_from_sw; /* only for xgmac */ +}; + +struct hns_mac_cb { + struct device *dev; + struct dsaf_device *dsaf_dev; + struct mac_priv priv; + u8 __iomem *vaddr; + u8 __iomem *cpld_vaddr; + u8 __iomem *sys_ctl_vaddr; + u8 __iomem *serdes_vaddr; + struct mac_entry_idx addr_entry_idx[DSAF_MAX_VM_NUM]; + u8 sfp_prsnt; + u8 cpld_led_value; + u8 mac_id; + + u8 link; + u8 half_duplex; + u16 speed; + u16 max_speed; + u16 max_frm; + u16 tx_pause_frm_time; + u32 if_support; + u64 txpkt_for_led; + u64 rxpkt_for_led; + enum hnae_port_type mac_type; + phy_interface_t phy_if; + enum hnae_loop loop_mode; + + struct device_node *phy_node; + + struct mac_hw_stats hw_stats; +}; + +struct mac_driver { + /*init Mac when init nic or dsaf*/ + void (*mac_init)(void *mac_drv); + /*remove mac when remove nic or dsaf*/ + void (*mac_free)(void *mac_drv); + /*enable mac when enable nic or dsaf*/ + void (*mac_enable)(void *mac_drv, enum mac_commom_mode mode); + /*disable mac when disable nic or dsaf*/ + void (*mac_disable)(void *mac_drv, enum mac_commom_mode mode); + /* config mac address*/ + void (*set_mac_addr)(void *mac_drv, char *mac_addr); + /*adjust mac mode of port,include speed and duplex*/ + int (*adjust_link)(void *mac_drv, enum mac_speed speed, + u32 full_duplex); + /* config autoegotaite mode of port*/ + void (*set_an_mode)(void *mac_drv, u8 enable); + /* config loopbank mode */ + int (*config_loopback)(void *mac_drv, enum hnae_loop loop_mode, + u8 enable); + /* config mtu*/ + void (*config_max_frame_length)(void *mac_drv, u16 newval); + /*config PAD and CRC enable */ + void (*config_pad_and_crc)(void *mac_drv, u8 newval); + /* config duplex mode*/ + void (*config_half_duplex)(void *mac_drv, u8 newval); + /*config tx pause time,if pause_time is zero,disable tx pause enable*/ + void (*set_tx_auto_pause_frames)(void *mac_drv, u16 pause_time); + /*config rx pause enable*/ + void (*set_rx_ignore_pause_frames)(void *mac_drv, u32 enable); + /* config rx mode for promiscuous*/ + int (*set_promiscuous)(void *mac_drv, u8 enable); + /* get mac id */ + void (*mac_get_id)(void *mac_drv, u8 *mac_id); + void (*mac_pausefrm_cfg)(void *mac_drv, u32 rx_en, u32 tx_en); + + void (*autoneg_stat)(void *mac_drv, u32 *enable); + int (*set_pause_enable)(void *mac_drv, u32 rx_en, u32 tx_en); + void (*get_pause_enable)(void *mac_drv, u32 *rx_en, u32 *tx_en); + void (*get_link_status)(void *mac_drv, u32 *link_stat); + /* get the imporant regs*/ + void (*get_regs)(void *mac_drv, void *data); + int (*get_regs_count)(void); + /* get strings name for ethtool statistic */ + void (*get_strings)(u32 stringset, u8 *data); + /* get the number of strings*/ + int (*get_sset_count)(int stringset); + + /* get the statistic by ethtools*/ + void (*get_ethtool_stats)(void *mac_drv, u64 *data); + + /* get mac information */ + void (*get_info)(void *mac_drv, struct mac_info *mac_info); + + void (*update_stats)(void *mac_drv); + + enum mac_mode mac_mode; + u8 mac_id; + struct hns_mac_cb *mac_cb; + void __iomem *io_base; + unsigned int mac_en_flg;/*you'd better don't enable mac twice*/ + unsigned int virt_dev_num; + struct device *dev; +}; + +struct mac_stats_string { + char desc[64]; + unsigned long offset; +}; + +#define MAC_MAKE_MODE(interface, speed) (enum mac_mode)((interface) | (speed)) +#define MAC_INTERFACE_FROM_MODE(mode) (enum mac_intf)((mode) & 0xFFFF0000) +#define MAC_SPEED_FROM_MODE(mode) (enum mac_speed)((mode) & 0x0000FFFF) +#define MAC_STATS_FIELD_OFF(field) (offsetof(struct mac_hw_stats, field)) + +static inline struct mac_driver *hns_mac_get_drv( + const struct hns_mac_cb *mac_cb) +{ + return (struct mac_driver *)(mac_cb->priv.mac); +} + +void *hns_gmac_config(struct hns_mac_cb *mac_cb, + struct mac_params *mac_param); +void *hns_xgmac_config(struct hns_mac_cb *mac_cb, + struct mac_params *mac_param); + +int hns_mac_init(struct dsaf_device *dsaf_dev); +void mac_adjust_link(struct net_device *net_dev); +void hns_mac_get_link_status(struct hns_mac_cb *mac_cb, u32 *link_status); +int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, u32 vmid, char *addr); +int hns_mac_set_multi(struct hns_mac_cb *mac_cb, + u32 port_num, char *addr, u8 en); +int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vm, u8 en); +void hns_mac_start(struct hns_mac_cb *mac_cb); +void hns_mac_stop(struct hns_mac_cb *mac_cb); +int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac); +void hns_mac_uninit(struct dsaf_device *dsaf_dev); +void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex); +void hns_mac_reset(struct hns_mac_cb *mac_cb); +void hns_mac_get_autoneg(struct hns_mac_cb *mac_cb, u32 *auto_neg); +void hns_mac_get_pauseparam(struct hns_mac_cb *mac_cb, u32 *rx_en, u32 *tx_en); +int hns_mac_set_autoneg(struct hns_mac_cb *mac_cb, u8 enable); +int hns_mac_set_pauseparam(struct hns_mac_cb *mac_cb, u32 rx_en, u32 tx_en); +int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu); +int hns_mac_get_port_info(struct hns_mac_cb *mac_cb, + u8 *auto_neg, u16 *speed, u8 *duplex); +phy_interface_t hns_mac_get_phy_if(struct hns_mac_cb *mac_cb); +int hns_mac_config_sds_loopback(struct hns_mac_cb *mac_cb, u8 en); +int hns_mac_config_mac_loopback(struct hns_mac_cb *mac_cb, + enum hnae_loop loop, int en); +void hns_mac_update_stats(struct hns_mac_cb *mac_cb); +void hns_mac_get_stats(struct hns_mac_cb *mac_cb, u64 *data); +void hns_mac_get_strings(struct hns_mac_cb *mac_cb, int stringset, u8 *data); +int hns_mac_get_sset_count(struct hns_mac_cb *mac_cb, int stringset); +void hns_mac_get_regs(struct hns_mac_cb *mac_cb, void *data); +int hns_mac_get_regs_count(struct hns_mac_cb *mac_cb); +void hns_set_led_opt(struct hns_mac_cb *mac_cb); +int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status); +#endif /* _HNS_DSAF_MAC_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c new file mode 100644 index 000000000000..26ae6c64d74c --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -0,0 +1,2445 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/device.h> +#include "hns_dsaf_main.h" +#include "hns_dsaf_rcb.h" +#include "hns_dsaf_ppe.h" +#include "hns_dsaf_mac.h" + +const char *g_dsaf_mode_match[DSAF_MODE_MAX] = { + [DSAF_MODE_DISABLE_2PORT_64VM] = "2port-64vf", + [DSAF_MODE_DISABLE_6PORT_0VM] = "6port-16rss", + [DSAF_MODE_DISABLE_6PORT_16VM] = "6port-16vf", +}; + +int hns_dsaf_get_cfg(struct dsaf_device *dsaf_dev) +{ + int ret, i; + u32 desc_num; + u32 buf_size; + const char *name, *mode_str; + struct device_node *np = dsaf_dev->dev->of_node; + + if (of_device_is_compatible(np, "hisilicon,hns-dsaf-v2")) + dsaf_dev->dsaf_ver = AE_VERSION_2; + else + dsaf_dev->dsaf_ver = AE_VERSION_1; + + ret = of_property_read_string(np, "dsa_name", &name); + if (ret) { + dev_err(dsaf_dev->dev, "get dsaf name fail, ret=%d!\n", ret); + return ret; + } + strncpy(dsaf_dev->ae_dev.name, name, AE_NAME_SIZE); + dsaf_dev->ae_dev.name[AE_NAME_SIZE - 1] = '\0'; + + ret = of_property_read_string(np, "mode", &mode_str); + if (ret) { + dev_err(dsaf_dev->dev, "get dsaf mode fail, ret=%d!\n", ret); + return ret; + } + for (i = 0; i < DSAF_MODE_MAX; i++) { + if (g_dsaf_mode_match[i] && + !strcmp(mode_str, g_dsaf_mode_match[i])) + break; + } + if (i >= DSAF_MODE_MAX || + i == DSAF_MODE_INVALID || i == DSAF_MODE_ENABLE) { + dev_err(dsaf_dev->dev, + "%s prs mode str fail!\n", dsaf_dev->ae_dev.name); + return -EINVAL; + } + dsaf_dev->dsaf_mode = (enum dsaf_mode)i; + + if (dsaf_dev->dsaf_mode > DSAF_MODE_ENABLE) + dsaf_dev->dsaf_en = HRD_DSAF_NO_DSAF_MODE; + else + dsaf_dev->dsaf_en = HRD_DSAF_MODE; + + if ((i == DSAF_MODE_ENABLE_16VM) || + (i == DSAF_MODE_DISABLE_2PORT_8VM) || + (i == DSAF_MODE_DISABLE_6PORT_2VM)) + dsaf_dev->dsaf_tc_mode = HRD_DSAF_8TC_MODE; + else + dsaf_dev->dsaf_tc_mode = HRD_DSAF_4TC_MODE; + + dsaf_dev->sc_base = of_iomap(np, 0); + if (!dsaf_dev->sc_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 0 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->sds_base = of_iomap(np, 1); + if (!dsaf_dev->sds_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 1 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->ppe_base = of_iomap(np, 2); + if (!dsaf_dev->ppe_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 2 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->io_base = of_iomap(np, 3); + if (!dsaf_dev->io_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 3 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->cpld_base = of_iomap(np, 4); + if (!dsaf_dev->cpld_base) + dev_dbg(dsaf_dev->dev, "NO CPLD ADDR"); + + ret = of_property_read_u32(np, "desc-num", &desc_num); + if (ret < 0 || desc_num < HNS_DSAF_MIN_DESC_CNT || + desc_num > HNS_DSAF_MAX_DESC_CNT) { + dev_err(dsaf_dev->dev, "get desc-num(%d) fail, ret=%d!\n", + desc_num, ret); + goto unmap_base_addr; + } + dsaf_dev->desc_num = desc_num; + + ret = of_property_read_u32(np, "buf-size", &buf_size); + if (ret < 0) { + dev_err(dsaf_dev->dev, + "get buf-size fail, ret=%d!\r\n", ret); + goto unmap_base_addr; + } + dsaf_dev->buf_size = buf_size; + + dsaf_dev->buf_size_type = hns_rcb_buf_size2type(buf_size); + if (dsaf_dev->buf_size_type < 0) { + dev_err(dsaf_dev->dev, + "buf_size(%d) is wrong!\n", buf_size); + goto unmap_base_addr; + } + + if (!dma_set_mask_and_coherent(dsaf_dev->dev, DMA_BIT_MASK(64ULL))) + dev_dbg(dsaf_dev->dev, "set mask to 64bit\n"); + else + dev_err(dsaf_dev->dev, "set mask to 64bit fail!\n"); + + return 0; + +unmap_base_addr: + if (dsaf_dev->io_base) + iounmap(dsaf_dev->io_base); + if (dsaf_dev->ppe_base) + iounmap(dsaf_dev->ppe_base); + if (dsaf_dev->sds_base) + iounmap(dsaf_dev->sds_base); + if (dsaf_dev->sc_base) + iounmap(dsaf_dev->sc_base); + if (dsaf_dev->cpld_base) + iounmap(dsaf_dev->cpld_base); + return ret; +} + +static void hns_dsaf_free_cfg(struct dsaf_device *dsaf_dev) +{ + if (dsaf_dev->io_base) + iounmap(dsaf_dev->io_base); + + if (dsaf_dev->ppe_base) + iounmap(dsaf_dev->ppe_base); + + if (dsaf_dev->sds_base) + iounmap(dsaf_dev->sds_base); + + if (dsaf_dev->sc_base) + iounmap(dsaf_dev->sc_base); + + if (dsaf_dev->cpld_base) + iounmap(dsaf_dev->cpld_base); +} + +/** + * hns_dsaf_sbm_link_sram_init_en - config dsaf_sbm_init_en + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_sbm_link_sram_init_en(struct dsaf_device *dsaf_dev) +{ + dsaf_set_dev_bit(dsaf_dev, DSAF_CFG_0_REG, DSAF_CFG_SBM_INIT_S, 1); +} + +/** + * hns_dsaf_reg_cnt_clr_ce - config hns_dsaf_reg_cnt_clr_ce + * @dsaf_id: dsa fabric id + * @hns_dsaf_reg_cnt_clr_ce: config value + */ +static void +hns_dsaf_reg_cnt_clr_ce(struct dsaf_device *dsaf_dev, u32 reg_cnt_clr_ce) +{ + dsaf_set_dev_bit(dsaf_dev, DSAF_DSA_REG_CNT_CLR_CE_REG, + DSAF_CNT_CLR_CE_S, reg_cnt_clr_ce); +} + +/** + * hns_ppe_qid_cfg - config ppe qid + * @dsaf_id: dsa fabric id + * @pppe_qid_cfg: value array + */ +static void +hns_dsaf_ppe_qid_cfg(struct dsaf_device *dsaf_dev, u32 qid_cfg) +{ + u32 i; + + for (i = 0; i < DSAF_COMM_CHN; i++) { + dsaf_set_dev_field(dsaf_dev, + DSAF_PPE_QID_CFG_0_REG + 0x0004 * i, + DSAF_PPE_QID_CFG_M, DSAF_PPE_QID_CFG_S, + qid_cfg); + } +} + +/** + * hns_dsaf_sw_port_type_cfg - cfg sw type + * @dsaf_id: dsa fabric id + * @psw_port_type: array + */ +static void hns_dsaf_sw_port_type_cfg(struct dsaf_device *dsaf_dev, + enum dsaf_sw_port_type port_type) +{ + u32 i; + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) { + dsaf_set_dev_field(dsaf_dev, + DSAF_SW_PORT_TYPE_0_REG + 0x0004 * i, + DSAF_SW_PORT_TYPE_M, DSAF_SW_PORT_TYPE_S, + port_type); + } +} + +/** + * hns_dsaf_stp_port_type_cfg - cfg stp type + * @dsaf_id: dsa fabric id + * @pstp_port_type: array + */ +static void hns_dsaf_stp_port_type_cfg(struct dsaf_device *dsaf_dev, + enum dsaf_stp_port_type port_type) +{ + u32 i; + + for (i = 0; i < DSAF_COMM_CHN; i++) { + dsaf_set_dev_field(dsaf_dev, + DSAF_STP_PORT_TYPE_0_REG + 0x0004 * i, + DSAF_STP_PORT_TYPE_M, DSAF_STP_PORT_TYPE_S, + port_type); + } +} + +/** + * hns_dsaf_sbm_cfg - config sbm + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_sbm_cfg(struct dsaf_device *dsaf_dev) +{ + u32 o_sbm_cfg; + u32 i; + + for (i = 0; i < DSAF_SBM_NUM; i++) { + o_sbm_cfg = dsaf_read_dev(dsaf_dev, + DSAF_SBM_CFG_REG_0_REG + 0x80 * i); + dsaf_set_bit(o_sbm_cfg, DSAF_SBM_CFG_EN_S, 1); + dsaf_set_bit(o_sbm_cfg, DSAF_SBM_CFG_SHCUT_EN_S, 0); + dsaf_write_dev(dsaf_dev, + DSAF_SBM_CFG_REG_0_REG + 0x80 * i, o_sbm_cfg); + } +} + +/** + * hns_dsaf_sbm_cfg_mib_en - config sbm + * @dsaf_id: dsa fabric id + */ +static int hns_dsaf_sbm_cfg_mib_en(struct dsaf_device *dsaf_dev) +{ + u32 sbm_cfg_mib_en; + u32 i; + u32 reg; + u32 read_cnt; + + for (i = 0; i < DSAF_SBM_NUM; i++) { + reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i; + dsaf_set_dev_bit(dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S, 1); + } + + /* waitint for all sbm enable finished */ + for (i = 0; i < DSAF_SBM_NUM; i++) { + read_cnt = 0; + reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i; + do { + udelay(1); + sbm_cfg_mib_en = dsaf_get_dev_bit( + dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S); + read_cnt++; + } while (sbm_cfg_mib_en == 0 && + read_cnt < DSAF_CFG_READ_CNT); + + if (sbm_cfg_mib_en == 0) { + dev_err(dsaf_dev->dev, + "sbm_cfg_mib_en fail,%s,sbm_num=%d\n", + dsaf_dev->ae_dev.name, i); + return -ENODEV; + } + } + + return 0; +} + +/** + * hns_dsaf_sbm_bp_wl_cfg - config sbm + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev) +{ + u32 o_sbm_bp_cfg0; + u32 o_sbm_bp_cfg1; + u32 o_sbm_bp_cfg2; + u32 o_sbm_bp_cfg3; + u32 reg; + u32 i; + + /* XGE */ + for (i = 0; i < DSAF_XGE_NUM; i++) { + reg = DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg0 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M, + DSAF_SBM_CFG0_COM_MAX_BUF_NUM_S, 512); + dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M, + DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_S, 0); + dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M, + DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_S, 0); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg0); + + reg = DSAF_SBM_BP_CFG_1_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg1 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M, + DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_S, 0); + dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M, + DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_S, 0); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg1); + + reg = DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + DSAF_SBM_CFG2_SET_BUF_NUM_S, 104); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + DSAF_SBM_CFG2_RESET_BUF_NUM_S, 128); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + + reg = DSAF_SBM_BP_CFG_3_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 110); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 160); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3); + + /* for no enable pfc mode */ + reg = DSAF_SBM_BP_CFG_4_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 128); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 192); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3); + } + + /* PPE */ + for (i = 0; i < DSAF_COMM_CHN; i++) { + reg = DSAF_SBM_BP_CFG_2_PPE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + DSAF_SBM_CFG2_SET_BUF_NUM_S, 10); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + DSAF_SBM_CFG2_RESET_BUF_NUM_S, 12); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + } + + /* RoCEE */ + for (i = 0; i < DSAF_COMM_CHN; i++) { + reg = DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + DSAF_SBM_CFG2_SET_BUF_NUM_S, 2); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + DSAF_SBM_CFG2_RESET_BUF_NUM_S, 4); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + } +} + +/** + * hns_dsaf_voq_bp_all_thrd_cfg - voq + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_voq_bp_all_thrd_cfg(struct dsaf_device *dsaf_dev) +{ + u32 voq_bp_all_thrd; + u32 i; + + for (i = 0; i < DSAF_VOQ_NUM; i++) { + voq_bp_all_thrd = dsaf_read_dev( + dsaf_dev, DSAF_VOQ_BP_ALL_THRD_0_REG + 0x40 * i); + if (i < DSAF_XGE_NUM) { + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_DOWNTHRD_M, + DSAF_VOQ_BP_ALL_DOWNTHRD_S, 930); + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_UPTHRD_M, + DSAF_VOQ_BP_ALL_UPTHRD_S, 950); + } else { + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_DOWNTHRD_M, + DSAF_VOQ_BP_ALL_DOWNTHRD_S, 220); + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_UPTHRD_M, + DSAF_VOQ_BP_ALL_UPTHRD_S, 230); + } + dsaf_write_dev( + dsaf_dev, DSAF_VOQ_BP_ALL_THRD_0_REG + 0x40 * i, + voq_bp_all_thrd); + } +} + +/** + * hns_dsaf_tbl_tcam_data_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_tcam_data: addr + */ +static void hns_dsaf_tbl_tcam_data_cfg( + struct dsaf_device *dsaf_dev, + struct dsaf_tbl_tcam_data *ptbl_tcam_data) +{ + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_LOW_0_REG, + ptbl_tcam_data->tbl_tcam_data_low); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_HIGH_0_REG, + ptbl_tcam_data->tbl_tcam_data_high); +} + +/** + * dsaf_tbl_tcam_mcast_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_tcam_mcast: addr + */ +static void hns_dsaf_tbl_tcam_mcast_cfg( + struct dsaf_device *dsaf_dev, + struct dsaf_tbl_tcam_mcast_cfg *mcast) +{ + u32 mcast_cfg4; + + mcast_cfg4 = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG); + dsaf_set_bit(mcast_cfg4, DSAF_TBL_MCAST_CFG4_ITEM_VLD_S, + mcast->tbl_mcast_item_vld); + dsaf_set_bit(mcast_cfg4, DSAF_TBL_MCAST_CFG4_OLD_EN_S, + mcast->tbl_mcast_old_en); + dsaf_set_field(mcast_cfg4, DSAF_TBL_MCAST_CFG4_VM128_112_M, + DSAF_TBL_MCAST_CFG4_VM128_112_S, + mcast->tbl_mcast_port_msk[4]); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG, mcast_cfg4); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG, + mcast->tbl_mcast_port_msk[3]); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG, + mcast->tbl_mcast_port_msk[2]); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG, + mcast->tbl_mcast_port_msk[1]); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG, + mcast->tbl_mcast_port_msk[0]); +} + +/** + * hns_dsaf_tbl_tcam_ucast_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_tcam_ucast: addr + */ +static void hns_dsaf_tbl_tcam_ucast_cfg( + struct dsaf_device *dsaf_dev, + struct dsaf_tbl_tcam_ucast_cfg *tbl_tcam_ucast) +{ + u32 ucast_cfg1; + + ucast_cfg1 = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_UCAST_CFG_0_REG); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S, + tbl_tcam_ucast->tbl_ucast_mac_discard); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_ITEM_VLD_S, + tbl_tcam_ucast->tbl_ucast_item_vld); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_OLD_EN_S, + tbl_tcam_ucast->tbl_ucast_old_en); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_DVC_S, + tbl_tcam_ucast->tbl_ucast_dvc); + dsaf_set_field(ucast_cfg1, DSAF_TBL_UCAST_CFG1_OUT_PORT_M, + DSAF_TBL_UCAST_CFG1_OUT_PORT_S, + tbl_tcam_ucast->tbl_ucast_out_port); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_UCAST_CFG_0_REG, ucast_cfg1); +} + +/** + * hns_dsaf_tbl_line_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_lin: addr + */ +static void hns_dsaf_tbl_line_cfg(struct dsaf_device *dsaf_dev, + struct dsaf_tbl_line_cfg *tbl_lin) +{ + u32 tbl_line; + + tbl_line = dsaf_read_dev(dsaf_dev, DSAF_TBL_LIN_CFG_0_REG); + dsaf_set_bit(tbl_line, DSAF_TBL_LINE_CFG_MAC_DISCARD_S, + tbl_lin->tbl_line_mac_discard); + dsaf_set_bit(tbl_line, DSAF_TBL_LINE_CFG_DVC_S, + tbl_lin->tbl_line_dvc); + dsaf_set_field(tbl_line, DSAF_TBL_LINE_CFG_OUT_PORT_M, + DSAF_TBL_LINE_CFG_OUT_PORT_S, + tbl_lin->tbl_line_out_port); + dsaf_write_dev(dsaf_dev, DSAF_TBL_LIN_CFG_0_REG, tbl_line); +} + +/** + * hns_dsaf_tbl_tcam_mcast_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_mcast_pul(struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +/** + * hns_dsaf_tbl_line_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_line_pul(struct dsaf_device *dsaf_dev) +{ + u32 tbl_pul; + + tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(tbl_pul, DSAF_TBL_PUL_LINE_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, tbl_pul); + dsaf_set_bit(tbl_pul, DSAF_TBL_PUL_LINE_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, tbl_pul); +} + +/** + * hns_dsaf_tbl_tcam_data_mcast_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_data_mcast_pul( + struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 1); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 0); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +/** + * hns_dsaf_tbl_tcam_data_ucast_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_data_ucast_pul( + struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 1); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_UCAST_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 0); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_UCAST_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +/** + * hns_dsaf_tbl_stat_en - tbl + * @dsaf_id: dsa fabric id + * @ptbl_stat_en: addr + */ +static void hns_dsaf_tbl_stat_en(struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_ctrl; + + o_tbl_ctrl = dsaf_read_dev(dsaf_dev, DSAF_TBL_DFX_CTRL_0_REG); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_LINE_LKUP_NUM_EN_S, 1); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_UC_LKUP_NUM_EN_S, 1); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_MC_LKUP_NUM_EN_S, 1); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_BC_LKUP_NUM_EN_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_DFX_CTRL_0_REG, o_tbl_ctrl); +} + +/** + * hns_dsaf_rocee_bp_en - rocee back press enable + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_rocee_bp_en(struct dsaf_device *dsaf_dev) +{ + dsaf_set_dev_bit(dsaf_dev, DSAF_XGE_CTRL_SIG_CFG_0_REG, + DSAF_FC_XGE_TX_PAUSE_S, 1); +} + +/* set msk for dsaf exception irq*/ +static void hns_dsaf_int_xge_msk_set(struct dsaf_device *dsaf_dev, + u32 chnn_num, u32 mask_set) +{ + dsaf_write_dev(dsaf_dev, + DSAF_XGE_INT_MSK_0_REG + 0x4 * chnn_num, mask_set); +} + +static void hns_dsaf_int_ppe_msk_set(struct dsaf_device *dsaf_dev, + u32 chnn_num, u32 msk_set) +{ + dsaf_write_dev(dsaf_dev, + DSAF_PPE_INT_MSK_0_REG + 0x4 * chnn_num, msk_set); +} + +static void hns_dsaf_int_rocee_msk_set(struct dsaf_device *dsaf_dev, + u32 chnn, u32 msk_set) +{ + dsaf_write_dev(dsaf_dev, + DSAF_ROCEE_INT_MSK_0_REG + 0x4 * chnn, msk_set); +} + +static void +hns_dsaf_int_tbl_msk_set(struct dsaf_device *dsaf_dev, u32 msk_set) +{ + dsaf_write_dev(dsaf_dev, DSAF_TBL_INT_MSK_0_REG, msk_set); +} + +/* clr dsaf exception irq*/ +static void hns_dsaf_int_xge_src_clr(struct dsaf_device *dsaf_dev, + u32 chnn_num, u32 int_src) +{ + dsaf_write_dev(dsaf_dev, + DSAF_XGE_INT_SRC_0_REG + 0x4 * chnn_num, int_src); +} + +static void hns_dsaf_int_ppe_src_clr(struct dsaf_device *dsaf_dev, + u32 chnn, u32 int_src) +{ + dsaf_write_dev(dsaf_dev, + DSAF_PPE_INT_SRC_0_REG + 0x4 * chnn, int_src); +} + +static void hns_dsaf_int_rocee_src_clr(struct dsaf_device *dsaf_dev, + u32 chnn, u32 int_src) +{ + dsaf_write_dev(dsaf_dev, + DSAF_ROCEE_INT_SRC_0_REG + 0x4 * chnn, int_src); +} + +static void hns_dsaf_int_tbl_src_clr(struct dsaf_device *dsaf_dev, + u32 int_src) +{ + dsaf_write_dev(dsaf_dev, DSAF_TBL_INT_SRC_0_REG, int_src); +} + +/** + * hns_dsaf_single_line_tbl_cfg - INT + * @dsaf_id: dsa fabric id + * @address: + * @ptbl_line: + */ +static void hns_dsaf_single_line_tbl_cfg( + struct dsaf_device *dsaf_dev, + u32 address, struct dsaf_tbl_line_cfg *ptbl_line) +{ + /*Write Addr*/ + hns_dsaf_tbl_line_addr_cfg(dsaf_dev, address); + + /*Write Line*/ + hns_dsaf_tbl_line_cfg(dsaf_dev, ptbl_line); + + /*Write Plus*/ + hns_dsaf_tbl_line_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_uc_cfg - INT + * @dsaf_id: dsa fabric id + * @address, + * @ptbl_tcam_data, + */ +static void hns_dsaf_tcam_uc_cfg( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_ucast_cfg *ptbl_tcam_ucast) +{ + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + /*Write Tcam Data*/ + hns_dsaf_tbl_tcam_data_cfg(dsaf_dev, ptbl_tcam_data); + /*Write Tcam Ucast*/ + hns_dsaf_tbl_tcam_ucast_cfg(dsaf_dev, ptbl_tcam_ucast); + /*Write Plus*/ + hns_dsaf_tbl_tcam_data_ucast_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_mc_cfg - INT + * @dsaf_id: dsa fabric id + * @address, + * @ptbl_tcam_data, + * @ptbl_tcam_mcast, + */ +static void hns_dsaf_tcam_mc_cfg( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_mcast_cfg *ptbl_tcam_mcast) +{ + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + /*Write Tcam Data*/ + hns_dsaf_tbl_tcam_data_cfg(dsaf_dev, ptbl_tcam_data); + /*Write Tcam Mcast*/ + hns_dsaf_tbl_tcam_mcast_cfg(dsaf_dev, ptbl_tcam_mcast); + /*Write Plus*/ + hns_dsaf_tbl_tcam_data_mcast_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_mc_invld - INT + * @dsaf_id: dsa fabric id + * @address + */ +static void hns_dsaf_tcam_mc_invld(struct dsaf_device *dsaf_dev, u32 address) +{ + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + + /*write tcam mcast*/ + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG, 0); + + /*Write Plus*/ + hns_dsaf_tbl_tcam_mcast_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_uc_get - INT + * @dsaf_id: dsa fabric id + * @address + * @ptbl_tcam_data + * @ptbl_tcam_ucast + */ +static void hns_dsaf_tcam_uc_get( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_ucast_cfg *ptbl_tcam_ucast) +{ + u32 tcam_read_data0; + u32 tcam_read_data4; + + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + + /*read tcam item puls*/ + hns_dsaf_tbl_tcam_load_pul(dsaf_dev); + + /*read tcam data*/ + ptbl_tcam_data->tbl_tcam_data_high + = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_LOW_0_REG); + ptbl_tcam_data->tbl_tcam_data_low + = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG); + + /*read tcam mcast*/ + tcam_read_data0 = dsaf_read_dev(dsaf_dev, + DSAF_TBL_TCAM_RAM_RDATA0_0_REG); + tcam_read_data4 = dsaf_read_dev(dsaf_dev, + DSAF_TBL_TCAM_RAM_RDATA4_0_REG); + + ptbl_tcam_ucast->tbl_ucast_item_vld + = dsaf_get_bit(tcam_read_data4, + DSAF_TBL_MCAST_CFG4_ITEM_VLD_S); + ptbl_tcam_ucast->tbl_ucast_old_en + = dsaf_get_bit(tcam_read_data4, DSAF_TBL_MCAST_CFG4_OLD_EN_S); + ptbl_tcam_ucast->tbl_ucast_mac_discard + = dsaf_get_bit(tcam_read_data0, + DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S); + ptbl_tcam_ucast->tbl_ucast_out_port + = dsaf_get_field(tcam_read_data0, + DSAF_TBL_UCAST_CFG1_OUT_PORT_M, + DSAF_TBL_UCAST_CFG1_OUT_PORT_S); + ptbl_tcam_ucast->tbl_ucast_dvc + = dsaf_get_bit(tcam_read_data0, DSAF_TBL_UCAST_CFG1_DVC_S); +} + +/** + * hns_dsaf_tcam_mc_get - INT + * @dsaf_id: dsa fabric id + * @address + * @ptbl_tcam_data + * @ptbl_tcam_ucast + */ +static void hns_dsaf_tcam_mc_get( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_mcast_cfg *ptbl_tcam_mcast) +{ + u32 data_tmp; + + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + + /*read tcam item puls*/ + hns_dsaf_tbl_tcam_load_pul(dsaf_dev); + + /*read tcam data*/ + ptbl_tcam_data->tbl_tcam_data_high = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_LOW_0_REG); + ptbl_tcam_data->tbl_tcam_data_low = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG); + + /*read tcam mcast*/ + ptbl_tcam_mcast->tbl_mcast_port_msk[0] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA0_0_REG); + ptbl_tcam_mcast->tbl_mcast_port_msk[1] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA1_0_REG); + ptbl_tcam_mcast->tbl_mcast_port_msk[2] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA2_0_REG); + ptbl_tcam_mcast->tbl_mcast_port_msk[3] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA3_0_REG); + + data_tmp = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA4_0_REG); + ptbl_tcam_mcast->tbl_mcast_item_vld = + dsaf_get_bit(data_tmp, DSAF_TBL_MCAST_CFG4_ITEM_VLD_S); + ptbl_tcam_mcast->tbl_mcast_old_en = + dsaf_get_bit(data_tmp, DSAF_TBL_MCAST_CFG4_OLD_EN_S); + ptbl_tcam_mcast->tbl_mcast_port_msk[4] = + dsaf_get_field(data_tmp, DSAF_TBL_MCAST_CFG4_VM128_112_M, + DSAF_TBL_MCAST_CFG4_VM128_112_S); +} + +/** + * hns_dsaf_tbl_line_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_line_init(struct dsaf_device *dsaf_dev) +{ + u32 i; + /* defaultly set all lineal mac table entry resulting discard */ + struct dsaf_tbl_line_cfg tbl_line[] = {{1, 0, 0} }; + + for (i = 0; i < DSAF_LINE_SUM; i++) + hns_dsaf_single_line_tbl_cfg(dsaf_dev, i, tbl_line); +} + +/** + * hns_dsaf_tbl_tcam_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_init(struct dsaf_device *dsaf_dev) +{ + u32 i; + struct dsaf_tbl_tcam_data tcam_data[] = {{0, 0} }; + struct dsaf_tbl_tcam_ucast_cfg tcam_ucast[] = {{0, 0, 0, 0, 0} }; + + /*tcam tbl*/ + for (i = 0; i < DSAF_TCAM_SUM; i++) + hns_dsaf_tcam_uc_cfg(dsaf_dev, i, tcam_data, tcam_ucast); +} + +/** + * hns_dsaf_pfc_en_cfg - dsaf pfc pause cfg + * @mac_cb: mac contrl block + */ +static void hns_dsaf_pfc_en_cfg(struct dsaf_device *dsaf_dev, + int mac_id, int en) +{ + if (!en) + dsaf_write_dev(dsaf_dev, DSAF_PFC_EN_0_REG + mac_id * 4, 0); + else + dsaf_write_dev(dsaf_dev, DSAF_PFC_EN_0_REG + mac_id * 4, 0xff); +} + +/** + * hns_dsaf_tbl_tcam_init - INT + * @dsaf_id: dsa fabric id + * @dsaf_mode + */ +static void hns_dsaf_comm_init(struct dsaf_device *dsaf_dev) +{ + u32 i; + u32 o_dsaf_cfg; + + o_dsaf_cfg = dsaf_read_dev(dsaf_dev, DSAF_CFG_0_REG); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_EN_S, dsaf_dev->dsaf_en); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_TC_MODE_S, dsaf_dev->dsaf_tc_mode); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_CRC_EN_S, 0); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_MIX_MODE_S, 0); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_LOCA_ADDR_EN_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_CFG_0_REG, o_dsaf_cfg); + + hns_dsaf_reg_cnt_clr_ce(dsaf_dev, 1); + hns_dsaf_stp_port_type_cfg(dsaf_dev, DSAF_STP_PORT_TYPE_FORWARD); + + /* set 22 queue per tx ppe engine, only used in switch mode */ + hns_dsaf_ppe_qid_cfg(dsaf_dev, DSAF_DEFAUTL_QUEUE_NUM_PER_PPE); + + /* in non switch mode, set all port to access mode */ + hns_dsaf_sw_port_type_cfg(dsaf_dev, DSAF_SW_PORT_TYPE_NON_VLAN); + + /*set dsaf pfc to 0 for parseing rx pause*/ + for (i = 0; i < DSAF_COMM_CHN; i++) + hns_dsaf_pfc_en_cfg(dsaf_dev, i, 0); + + /*msk and clr exception irqs */ + for (i = 0; i < DSAF_COMM_CHN; i++) { + hns_dsaf_int_xge_src_clr(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_ppe_src_clr(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_rocee_src_clr(dsaf_dev, i, 0xfffffffful); + + hns_dsaf_int_xge_msk_set(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_ppe_msk_set(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_rocee_msk_set(dsaf_dev, i, 0xfffffffful); + } + hns_dsaf_int_tbl_src_clr(dsaf_dev, 0xfffffffful); + hns_dsaf_int_tbl_msk_set(dsaf_dev, 0xfffffffful); +} + +/** + * hns_dsaf_inode_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_inode_init(struct dsaf_device *dsaf_dev) +{ + u32 reg; + u32 tc_cfg; + u32 i; + + if (dsaf_dev->dsaf_tc_mode == HRD_DSAF_4TC_MODE) + tc_cfg = HNS_DSAF_I4TC_CFG; + else + tc_cfg = HNS_DSAF_I8TC_CFG; + + for (i = 0; i < DSAF_INODE_NUM; i++) { + reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i; + dsaf_set_dev_field(dsaf_dev, reg, DSAF_INODE_IN_PORT_NUM_M, + DSAF_INODE_IN_PORT_NUM_S, i % DSAF_XGE_NUM); + + reg = DSAF_INODE_PRI_TC_CFG_0_REG + 0x80 * i; + dsaf_write_dev(dsaf_dev, reg, tc_cfg); + } +} + +/** + * hns_dsaf_sbm_init - INT + * @dsaf_id: dsa fabric id + */ +static int hns_dsaf_sbm_init(struct dsaf_device *dsaf_dev) +{ + u32 flag; + u32 cnt = 0; + int ret; + + hns_dsaf_sbm_bp_wl_cfg(dsaf_dev); + + /* enable sbm chanel, disable sbm chanel shcut function*/ + hns_dsaf_sbm_cfg(dsaf_dev); + + /* enable sbm mib */ + ret = hns_dsaf_sbm_cfg_mib_en(dsaf_dev); + if (ret) { + dev_err(dsaf_dev->dev, + "hns_dsaf_sbm_cfg_mib_en fail,%s, ret=%d\n", + dsaf_dev->ae_dev.name, ret); + return ret; + } + + /* enable sbm initial link sram */ + hns_dsaf_sbm_link_sram_init_en(dsaf_dev); + + do { + usleep_range(200, 210);/*udelay(200);*/ + flag = dsaf_read_dev(dsaf_dev, DSAF_SRAM_INIT_OVER_0_REG); + cnt++; + } while (flag != DSAF_SRAM_INIT_FINISH_FLAG && cnt < DSAF_CFG_READ_CNT); + + if (flag != DSAF_SRAM_INIT_FINISH_FLAG) { + dev_err(dsaf_dev->dev, + "hns_dsaf_sbm_init fail %s, flag=%d, cnt=%d\n", + dsaf_dev->ae_dev.name, flag, cnt); + return -ENODEV; + } + + hns_dsaf_rocee_bp_en(dsaf_dev); + + return 0; +} + +/** + * hns_dsaf_tbl_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_init(struct dsaf_device *dsaf_dev) +{ + hns_dsaf_tbl_stat_en(dsaf_dev); + + hns_dsaf_tbl_tcam_init(dsaf_dev); + hns_dsaf_tbl_line_init(dsaf_dev); +} + +/** + * hns_dsaf_voq_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_voq_init(struct dsaf_device *dsaf_dev) +{ + hns_dsaf_voq_bp_all_thrd_cfg(dsaf_dev); +} + +/** + * hns_dsaf_init_hw - init dsa fabric hardware + * @dsaf_dev: dsa fabric device struct pointer + */ +static int hns_dsaf_init_hw(struct dsaf_device *dsaf_dev) +{ + int ret; + + dev_dbg(dsaf_dev->dev, + "hns_dsaf_init_hw begin %s !\n", dsaf_dev->ae_dev.name); + + hns_dsaf_rst(dsaf_dev, 0); + mdelay(10); + hns_dsaf_rst(dsaf_dev, 1); + + hns_dsaf_comm_init(dsaf_dev); + + /*init XBAR_INODE*/ + hns_dsaf_inode_init(dsaf_dev); + + /*init SBM*/ + ret = hns_dsaf_sbm_init(dsaf_dev); + if (ret) + return ret; + + /*init TBL*/ + hns_dsaf_tbl_init(dsaf_dev); + + /*init VOQ*/ + hns_dsaf_voq_init(dsaf_dev); + + return 0; +} + +/** + * hns_dsaf_remove_hw - uninit dsa fabric hardware + * @dsaf_dev: dsa fabric device struct pointer + */ +static void hns_dsaf_remove_hw(struct dsaf_device *dsaf_dev) +{ + /*reset*/ + hns_dsaf_rst(dsaf_dev, 0); +} + +/** + * hns_dsaf_init - init dsa fabric + * @dsaf_dev: dsa fabric device struct pointer + * retuen 0 - success , negative --fail + */ +static int hns_dsaf_init(struct dsaf_device *dsaf_dev) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + u32 i; + int ret; + + ret = hns_dsaf_init_hw(dsaf_dev); + if (ret) + return ret; + + /* malloc mem for tcam mac key(vlan+mac) */ + priv->soft_mac_tbl = vzalloc(sizeof(*priv->soft_mac_tbl) + * DSAF_TCAM_SUM); + if (!priv->soft_mac_tbl) { + ret = -ENOMEM; + goto remove_hw; + } + + /*all entry invall */ + for (i = 0; i < DSAF_TCAM_SUM; i++) + (priv->soft_mac_tbl + i)->index = DSAF_INVALID_ENTRY_IDX; + + return 0; + +remove_hw: + hns_dsaf_remove_hw(dsaf_dev); + return ret; +} + +/** + * hns_dsaf_free - free dsa fabric + * @dsaf_dev: dsa fabric device struct pointer + */ +static void hns_dsaf_free(struct dsaf_device *dsaf_dev) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + + hns_dsaf_remove_hw(dsaf_dev); + + /* free all mac mem */ + vfree(priv->soft_mac_tbl); + priv->soft_mac_tbl = NULL; +} + +/** + * hns_dsaf_find_soft_mac_entry - find dsa fabric soft entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_key: mac entry struct pointer + */ +static u16 hns_dsaf_find_soft_mac_entry( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_tbl_tcam_key *mac_key) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry; + u32 i; + + soft_mac_entry = priv->soft_mac_tbl; + for (i = 0; i < DSAF_TCAM_SUM; i++) { + /* invall tab entry */ + if ((soft_mac_entry->index != DSAF_INVALID_ENTRY_IDX) && + (soft_mac_entry->tcam_key.high.val == mac_key->high.val) && + (soft_mac_entry->tcam_key.low.val == mac_key->low.val)) + /* return find result --soft index */ + return soft_mac_entry->index; + + soft_mac_entry++; + } + return DSAF_INVALID_ENTRY_IDX; +} + +/** + * hns_dsaf_find_empty_mac_entry - search dsa fabric soft empty-entry + * @dsaf_dev: dsa fabric device struct pointer + */ +static u16 hns_dsaf_find_empty_mac_entry(struct dsaf_device *dsaf_dev) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry; + u32 i; + + soft_mac_entry = priv->soft_mac_tbl; + for (i = 0; i < DSAF_TCAM_SUM; i++) { + /* inv all entry */ + if (soft_mac_entry->index == DSAF_INVALID_ENTRY_IDX) + /* return find result --soft index */ + return i; + + soft_mac_entry++; + } + return DSAF_INVALID_ENTRY_IDX; +} + +/** + * hns_dsaf_set_mac_key - set mac key + * @dsaf_dev: dsa fabric device struct pointer + * @mac_key: tcam key pointer + * @vlan_id: vlan id + * @in_port_num: input port num + * @addr: mac addr + */ +static void hns_dsaf_set_mac_key( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_tbl_tcam_key *mac_key, u16 vlan_id, u8 in_port_num, + u8 *addr) +{ + u8 port; + + if (dsaf_dev->dsaf_mode <= DSAF_MODE_ENABLE) + /*DSAF mode : in port id fixed 0*/ + port = 0; + else + /*non-dsaf mode*/ + port = in_port_num; + + mac_key->high.bits.mac_0 = addr[0]; + mac_key->high.bits.mac_1 = addr[1]; + mac_key->high.bits.mac_2 = addr[2]; + mac_key->high.bits.mac_3 = addr[3]; + mac_key->low.bits.mac_4 = addr[4]; + mac_key->low.bits.mac_5 = addr[5]; + mac_key->low.bits.vlan = vlan_id; + mac_key->low.bits.port = port; +} + +/** + * hns_dsaf_set_mac_uc_entry - set mac uc-entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: uc-mac entry + */ +int hns_dsaf_set_mac_uc_entry( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_tbl_tcam_ucast_cfg mac_data; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + + /* mac addr check */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr) || + MAC_IS_BROADCAST(mac_entry->addr) || + MAC_IS_MULTICAST(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "set_uc %s Mac %02x:%02x:%02x:%02x:%02x:%02x err!\n", + dsaf_dev->ae_dev.name, mac_entry->addr[0], + mac_entry->addr[1], mac_entry->addr[2], + mac_entry->addr[3], mac_entry->addr[4], + mac_entry->addr[5]); + return -EINVAL; + } + + /* config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /* entry ie exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if has not inv entry,find a empty entry */ + entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /* has not empty,return error */ + dev_err(dsaf_dev->dev, + "set_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + } + + dev_dbg(dsaf_dev->dev, + "set_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /* config hardware entry */ + mac_data.tbl_ucast_item_vld = 1; + mac_data.tbl_ucast_mac_discard = 0; + mac_data.tbl_ucast_old_en = 0; + /* default config dvc to 0 */ + mac_data.tbl_ucast_dvc = 0; + mac_data.tbl_ucast_out_port = mac_entry->port_num; + hns_dsaf_tcam_uc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + + /* config software entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = entry_index; + soft_mac_entry->tcam_key.high.val = mac_key.high.val; + soft_mac_entry->tcam_key.low.val = mac_key.low.val; + + return 0; +} + +/** + * hns_dsaf_set_mac_mc_entry - set mac mc-entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mc-mac entry + */ +int hns_dsaf_set_mac_mc_entry( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + struct dsaf_drv_tbl_tcam_key tmp_mac_key; + + /* mac addr check */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "set uc %s Mac %02x:%02x:%02x:%02x:%02x:%02x err!\n", + dsaf_dev->ae_dev.name, mac_entry->addr[0], + mac_entry->addr[1], mac_entry->addr[2], + mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, + mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /* entry ie exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot, find enpty entry*/ + entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot empty, error*/ + dev_err(dsaf_dev->dev, + "set_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + + /* config hardware entry */ + memset(mac_data.tbl_mcast_port_msk, + 0, sizeof(mac_data.tbl_mcast_port_msk)); + } else { + /* config hardware entry */ + hns_dsaf_tcam_mc_get( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + } + mac_data.tbl_mcast_old_en = 0; + mac_data.tbl_mcast_item_vld = 1; + dsaf_set_field(mac_data.tbl_mcast_port_msk[0], + 0x3F, 0, mac_entry->port_mask[0]); + + dev_dbg(dsaf_dev->dev, + "set_uc_entry, %s key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + hns_dsaf_tcam_mc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + + /* config software entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = entry_index; + soft_mac_entry->tcam_key.high.val = mac_key.high.val; + soft_mac_entry->tcam_key.low.val = mac_key.low.val; + + return 0; +} + +/** + * hns_dsaf_add_mac_mc_port - add mac mc-port + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mc-mac entry + */ +int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + struct dsaf_drv_tbl_tcam_key tmp_mac_key; + int mskid; + + /*chechk mac addr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "set_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x!\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key( + dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + memset(&mac_data, 0, sizeof(struct dsaf_tbl_tcam_mcast_cfg)); + + /*check exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot , find a empty*/ + entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot empty, error*/ + dev_err(dsaf_dev->dev, + "set_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val); + return -EINVAL; + } + } else { + /*if exist, add in */ + hns_dsaf_tcam_mc_get( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + } + /* config hardware entry */ + if (mac_entry->port_num < DSAF_SERVICE_NW_NUM) { + mskid = mac_entry->port_num; + } else if (mac_entry->port_num >= DSAF_BASE_INNER_PORT_NUM) { + mskid = mac_entry->port_num - + DSAF_BASE_INNER_PORT_NUM + DSAF_SERVICE_NW_NUM; + } else { + dev_err(dsaf_dev->dev, + "%s,pnum(%d)error,key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_entry->port_num, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dsaf_set_bit(mac_data.tbl_mcast_port_msk[mskid / 32], mskid % 32, 1); + mac_data.tbl_mcast_old_en = 0; + mac_data.tbl_mcast_item_vld = 1; + + dev_dbg(dsaf_dev->dev, + "set_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + hns_dsaf_tcam_mc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + + /*config software entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = entry_index; + soft_mac_entry->tcam_key.high.val = mac_key.high.val; + soft_mac_entry->tcam_key.low.val = mac_key.low.val; + + return 0; +} + +/** + * hns_dsaf_del_mac_entry - del mac mc-port + * @dsaf_dev: dsa fabric device struct pointer + * @vlan_id: vlian id + * @in_port_num: input port num + * @addr : mac addr + */ +int hns_dsaf_del_mac_entry(struct dsaf_device *dsaf_dev, u16 vlan_id, + u8 in_port_num, u8 *addr) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + + /*check mac addr */ + if (MAC_IS_ALL_ZEROS(addr) || MAC_IS_BROADCAST(addr)) { + dev_err(dsaf_dev->dev, + "del_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x!\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, vlan_id, in_port_num, addr); + + /*exist ?*/ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*not exist, error */ + dev_err(dsaf_dev->dev, + "del_mac_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dev_dbg(dsaf_dev->dev, + "del_mac_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*do del opt*/ + hns_dsaf_tcam_mc_invld(dsaf_dev, entry_index); + + /*del soft emtry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX; + + return 0; +} + +/** + * hns_dsaf_del_mac_mc_port - del mac mc- port + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mac entry + */ +int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + u16 vlan_id; + u8 in_port_num; + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_drv_tbl_tcam_key tmp_mac_key; + int mskid; + const u8 empty_msk[sizeof(mac_data.tbl_mcast_port_msk)] = {0}; + + if (!(void *)mac_entry) { + dev_err(dsaf_dev->dev, + "hns_dsaf_del_mac_mc_port mac_entry is NULL\n"); + return -EINVAL; + } + + /*get key info*/ + vlan_id = mac_entry->in_vlan_id; + in_port_num = mac_entry->in_port_num; + + /*check mac addr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "del_port failed, addr %02x:%02x:%02x:%02x:%02x:%02x!\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, vlan_id, in_port_num, + mac_entry->addr); + + /*check is exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*find none */ + dev_err(dsaf_dev->dev, + "find_soft_mac_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + + dev_dbg(dsaf_dev->dev, + "del_mac_mc_port, %s key(%#x:%#x) index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*read entry*/ + hns_dsaf_tcam_mc_get( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + + /*del the port*/ + if (mac_entry->port_num < DSAF_SERVICE_NW_NUM) { + mskid = mac_entry->port_num; + } else if (mac_entry->port_num >= DSAF_BASE_INNER_PORT_NUM) { + mskid = mac_entry->port_num - + DSAF_BASE_INNER_PORT_NUM + DSAF_SERVICE_NW_NUM; + } else { + dev_err(dsaf_dev->dev, + "%s,pnum(%d)error,key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_entry->port_num, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dsaf_set_bit(mac_data.tbl_mcast_port_msk[mskid / 32], mskid % 32, 0); + + /*check non port, do del entry */ + if (!memcmp(mac_data.tbl_mcast_port_msk, empty_msk, + sizeof(mac_data.tbl_mcast_port_msk))) { + hns_dsaf_tcam_mc_invld(dsaf_dev, entry_index); + + /* del soft entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX; + } else { /* not zer, just del port, updata*/ + hns_dsaf_tcam_mc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + } + + return 0; +} + +/** + * hns_dsaf_get_mac_uc_entry - get mac uc entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mac entry + */ +int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + + struct dsaf_tbl_tcam_ucast_cfg mac_data; + + /* check macaddr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr) || + MAC_IS_BROADCAST(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "get_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /*check exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*find none, error */ + dev_err(dsaf_dev->dev, + "get_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dev_dbg(dsaf_dev->dev, + "get_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*read entry*/ + hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + mac_entry->port_num = mac_data.tbl_ucast_out_port; + + return 0; +} + +/** + * hns_dsaf_get_mac_mc_entry - get mac mc entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mac entry + */ +int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + + struct dsaf_tbl_tcam_mcast_cfg mac_data; + + /*check mac addr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr) || + MAC_IS_BROADCAST(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "get_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /*check exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /* find none, error */ + dev_err(dsaf_dev->dev, + "get_mac_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val); + return -EINVAL; + } + dev_dbg(dsaf_dev->dev, + "get_mac_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*read entry */ + hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + + mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F; + return 0; +} + +/** + * hns_dsaf_get_mac_entry_by_index - get mac entry by tab index + * @dsaf_dev: dsa fabric device struct pointer + * @entry_index: tab entry index + * @mac_entry: mac entry + */ +int hns_dsaf_get_mac_entry_by_index( + struct dsaf_device *dsaf_dev, + u16 entry_index, struct dsaf_drv_mac_multi_dest_entry *mac_entry) +{ + struct dsaf_drv_tbl_tcam_key mac_key; + + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_tbl_tcam_ucast_cfg mac_uc_data; + char mac_addr[MAC_NUM_OCTETS_PER_ADDR] = {0}; + + if (entry_index >= DSAF_TCAM_SUM) { + /* find none, del error */ + dev_err(dsaf_dev->dev, "get_uc_entry failed, %s\n", + dsaf_dev->ae_dev.name); + return -EINVAL; + } + + /* mc entry, do read opt */ + hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + + mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F; + + /***get mac addr*/ + mac_addr[0] = mac_key.high.bits.mac_0; + mac_addr[1] = mac_key.high.bits.mac_1; + mac_addr[2] = mac_key.high.bits.mac_2; + mac_addr[3] = mac_key.high.bits.mac_3; + mac_addr[4] = mac_key.low.bits.mac_4; + mac_addr[5] = mac_key.low.bits.mac_5; + /**is mc or uc*/ + if (MAC_IS_MULTICAST((u8 *)mac_addr) || + MAC_IS_L3_MULTICAST((u8 *)mac_addr)) { + /**mc donot do*/ + } else { + /*is not mc, just uc... */ + hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, + &mac_uc_data); + mac_entry->port_mask[0] = (1 << mac_uc_data.tbl_ucast_out_port); + } + + return 0; +} + +static struct dsaf_device *hns_dsaf_alloc_dev(struct device *dev, + size_t sizeof_priv) +{ + struct dsaf_device *dsaf_dev; + + dsaf_dev = devm_kzalloc(dev, + sizeof(*dsaf_dev) + sizeof_priv, GFP_KERNEL); + if (unlikely(!dsaf_dev)) { + dsaf_dev = ERR_PTR(-ENOMEM); + } else { + dsaf_dev->dev = dev; + dev_set_drvdata(dev, dsaf_dev); + } + + return dsaf_dev; +} + +/** + * hns_dsaf_free_dev - free dev mem + * @dev: struct device pointer + */ +static void hns_dsaf_free_dev(struct dsaf_device *dsaf_dev) +{ + (void)dev_set_drvdata(dsaf_dev->dev, NULL); +} + +/** + * dsaf_pfc_unit_cnt - set pfc unit count + * @dsaf_id: dsa fabric id + * @pport_rate: value array + * @pdsaf_pfc_unit_cnt: value array + */ +static void hns_dsaf_pfc_unit_cnt(struct dsaf_device *dsaf_dev, int mac_id, + enum dsaf_port_rate_mode rate) +{ + u32 unit_cnt; + + switch (rate) { + case DSAF_PORT_RATE_10000: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_XGE; + break; + case DSAF_PORT_RATE_1000: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000; + break; + case DSAF_PORT_RATE_2500: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000; + break; + default: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_XGE; + } + + dsaf_set_dev_field(dsaf_dev, + (DSAF_PFC_UNIT_CNT_0_REG + 0x4 * (u64)mac_id), + DSAF_PFC_UNINT_CNT_M, DSAF_PFC_UNINT_CNT_S, + unit_cnt); +} + +/** + * dsaf_port_work_rate_cfg - fifo + * @dsaf_id: dsa fabric id + * @xge_ge_work_mode + */ +void hns_dsaf_port_work_rate_cfg(struct dsaf_device *dsaf_dev, int mac_id, + enum dsaf_port_rate_mode rate_mode) +{ + u32 port_work_mode; + + port_work_mode = dsaf_read_dev( + dsaf_dev, DSAF_XGE_GE_WORK_MODE_0_REG + 0x4 * (u64)mac_id); + + if (rate_mode == DSAF_PORT_RATE_10000) + dsaf_set_bit(port_work_mode, DSAF_XGE_GE_WORK_MODE_S, 1); + else + dsaf_set_bit(port_work_mode, DSAF_XGE_GE_WORK_MODE_S, 0); + + dsaf_write_dev(dsaf_dev, + DSAF_XGE_GE_WORK_MODE_0_REG + 0x4 * (u64)mac_id, + port_work_mode); + + hns_dsaf_pfc_unit_cnt(dsaf_dev, mac_id, rate_mode); +} + +/** + * hns_dsaf_fix_mac_mode - dsaf modify mac mode + * @mac_cb: mac contrl block + */ +void hns_dsaf_fix_mac_mode(struct hns_mac_cb *mac_cb) +{ + enum dsaf_port_rate_mode mode; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + int mac_id = mac_cb->mac_id; + + if (mac_cb->mac_type != HNAE_PORT_SERVICE) + return; + if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII) + mode = DSAF_PORT_RATE_10000; + else + mode = DSAF_PORT_RATE_1000; + + hns_dsaf_port_work_rate_cfg(dsaf_dev, mac_id, mode); +} + +void hns_dsaf_update_stats(struct dsaf_device *dsaf_dev, u32 node_num) +{ + struct dsaf_hw_stats *hw_stats + = &dsaf_dev->hw_stats[node_num]; + + hw_stats->pad_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_PAD_DISCARD_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->man_pkts += dsaf_read_dev(dsaf_dev, + DSAF_INODE_FINAL_IN_MAN_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rx_pkts += dsaf_read_dev(dsaf_dev, + DSAF_INODE_FINAL_IN_PKT_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rx_pkt_id += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SBM_PID_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rx_pause_frame += dsaf_read_dev(dsaf_dev, + DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->release_buf_num += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SBM_RELS_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->sbm_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SBM_DROP_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->crc_false += dsaf_read_dev(dsaf_dev, + DSAF_INODE_CRC_FALSE_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->bp_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_BP_DISCARD_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rslt_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_RSLT_DISCARD_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->local_addr_false += dsaf_read_dev(dsaf_dev, + DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG + 0x80 * (u64)node_num); + + hw_stats->vlan_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SW_VLAN_TAG_DISC_0_REG + 0x80 * (u64)node_num); + hw_stats->stp_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_IN_DATA_STP_DISC_0_REG + 0x80 * (u64)node_num); + + hw_stats->tx_pkts += dsaf_read_dev(dsaf_dev, + DSAF_XOD_RCVPKT_CNT_0_REG + 0x90 * (u64)node_num); +} + +/** + *hns_dsaf_get_regs - dump dsaf regs + *@dsaf_dev: dsaf device + *@data:data for value of regs + */ +void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data) +{ + u32 i = 0; + u32 j; + u32 *p = data; + + /* dsaf common registers */ + p[0] = dsaf_read_dev(ddev, DSAF_SRAM_INIT_OVER_0_REG); + p[1] = dsaf_read_dev(ddev, DSAF_CFG_0_REG); + p[2] = dsaf_read_dev(ddev, DSAF_ECC_ERR_INVERT_0_REG); + p[3] = dsaf_read_dev(ddev, DSAF_ABNORMAL_TIMEOUT_0_REG); + p[4] = dsaf_read_dev(ddev, DSAF_FSM_TIMEOUT_0_REG); + p[5] = dsaf_read_dev(ddev, DSAF_DSA_REG_CNT_CLR_CE_REG); + p[6] = dsaf_read_dev(ddev, DSAF_DSA_SBM_INF_FIFO_THRD_REG); + p[7] = dsaf_read_dev(ddev, DSAF_DSA_SRAM_1BIT_ECC_SEL_REG); + p[8] = dsaf_read_dev(ddev, DSAF_DSA_SRAM_1BIT_ECC_CNT_REG); + + p[9] = dsaf_read_dev(ddev, DSAF_PFC_EN_0_REG + port * 4); + p[10] = dsaf_read_dev(ddev, DSAF_PFC_UNIT_CNT_0_REG + port * 4); + p[11] = dsaf_read_dev(ddev, DSAF_XGE_INT_MSK_0_REG + port * 4); + p[12] = dsaf_read_dev(ddev, DSAF_XGE_INT_SRC_0_REG + port * 4); + p[13] = dsaf_read_dev(ddev, DSAF_XGE_INT_STS_0_REG + port * 4); + p[14] = dsaf_read_dev(ddev, DSAF_XGE_INT_MSK_0_REG + port * 4); + p[15] = dsaf_read_dev(ddev, DSAF_PPE_INT_MSK_0_REG + port * 4); + p[16] = dsaf_read_dev(ddev, DSAF_ROCEE_INT_MSK_0_REG + port * 4); + p[17] = dsaf_read_dev(ddev, DSAF_XGE_INT_SRC_0_REG + port * 4); + p[18] = dsaf_read_dev(ddev, DSAF_PPE_INT_SRC_0_REG + port * 4); + p[19] = dsaf_read_dev(ddev, DSAF_ROCEE_INT_SRC_0_REG + port * 4); + p[20] = dsaf_read_dev(ddev, DSAF_XGE_INT_STS_0_REG + port * 4); + p[21] = dsaf_read_dev(ddev, DSAF_PPE_INT_STS_0_REG + port * 4); + p[22] = dsaf_read_dev(ddev, DSAF_ROCEE_INT_STS_0_REG + port * 4); + p[23] = dsaf_read_dev(ddev, DSAF_PPE_QID_CFG_0_REG + port * 4); + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) + p[24 + i] = dsaf_read_dev(ddev, + DSAF_SW_PORT_TYPE_0_REG + i * 4); + + p[32] = dsaf_read_dev(ddev, DSAF_MIX_DEF_QID_0_REG + port * 4); + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) + p[33 + i] = dsaf_read_dev(ddev, + DSAF_PORT_DEF_VLAN_0_REG + i * 4); + + for (i = 0; i < DSAF_TOTAL_QUEUE_NUM; i++) + p[41 + i] = dsaf_read_dev(ddev, + DSAF_VM_DEF_VLAN_0_REG + i * 4); + + /* dsaf inode registers */ + p[170] = dsaf_read_dev(ddev, DSAF_INODE_CUT_THROUGH_CFG_0_REG); + + p[171] = dsaf_read_dev(ddev, + DSAF_INODE_ECC_ERR_ADDR_0_REG + port * 0x80); + + for (i = 0; i < DSAF_INODE_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[172 + i] = dsaf_read_dev(ddev, + DSAF_INODE_IN_PORT_NUM_0_REG + j * 0x80); + p[175 + i] = dsaf_read_dev(ddev, + DSAF_INODE_PRI_TC_CFG_0_REG + j * 0x80); + p[178 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BP_STATUS_0_REG + j * 0x80); + p[181 + i] = dsaf_read_dev(ddev, + DSAF_INODE_PAD_DISCARD_NUM_0_REG + j * 0x80); + p[184 + i] = dsaf_read_dev(ddev, + DSAF_INODE_FINAL_IN_MAN_NUM_0_REG + j * 0x80); + p[187 + i] = dsaf_read_dev(ddev, + DSAF_INODE_FINAL_IN_PKT_NUM_0_REG + j * 0x80); + p[190 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SBM_PID_NUM_0_REG + j * 0x80); + p[193 + i] = dsaf_read_dev(ddev, + DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG + j * 0x80); + p[196 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SBM_RELS_NUM_0_REG + j * 0x80); + p[199 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SBM_DROP_NUM_0_REG + j * 0x80); + p[202 + i] = dsaf_read_dev(ddev, + DSAF_INODE_CRC_FALSE_NUM_0_REG + j * 0x80); + p[205 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BP_DISCARD_NUM_0_REG + j * 0x80); + p[208 + i] = dsaf_read_dev(ddev, + DSAF_INODE_RSLT_DISCARD_NUM_0_REG + j * 0x80); + p[211 + i] = dsaf_read_dev(ddev, + DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG + j * 0x80); + p[214 + i] = dsaf_read_dev(ddev, + DSAF_INODE_VOQ_OVER_NUM_0_REG + j * 0x80); + p[217 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BD_SAVE_STATUS_0_REG + j * 4); + p[220 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BD_ORDER_STATUS_0_REG + j * 4); + p[223 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SW_VLAN_TAG_DISC_0_REG + j * 4); + p[224 + i] = dsaf_read_dev(ddev, + DSAF_INODE_IN_DATA_STP_DISC_0_REG + j * 4); + } + + p[227] = dsaf_read_dev(ddev, DSAF_INODE_GE_FC_EN_0_REG + port * 4); + + for (i = 0; i < DSAF_INODE_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[228 + i] = dsaf_read_dev(ddev, + DSAF_INODE_VC0_IN_PKT_NUM_0_REG + j * 4); + } + + p[231] = dsaf_read_dev(ddev, + DSAF_INODE_VC1_IN_PKT_NUM_0_REG + port * 4); + + /* dsaf inode registers */ + for (i = 0; i < DSAF_SBM_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[232 + i] = dsaf_read_dev(ddev, + DSAF_SBM_CFG_REG_0_REG + j * 0x80); + p[235 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + j * 0x80); + p[238 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_1_REG_0_REG + j * 0x80); + p[241 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + j * 0x80); + p[244 + i] = dsaf_read_dev(ddev, + DSAF_SBM_FREE_CNT_0_0_REG + j * 0x80); + p[245 + i] = dsaf_read_dev(ddev, + DSAF_SBM_FREE_CNT_1_0_REG + j * 0x80); + p[248 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_0_0_REG + j * 0x80); + p[251 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_1_0_REG + j * 0x80); + p[254 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_2_0_REG + j * 0x80); + p[257 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_3_0_REG + j * 0x80); + p[260 + i] = dsaf_read_dev(ddev, + DSAF_SBM_INER_ST_0_REG + j * 0x80); + p[263 + i] = dsaf_read_dev(ddev, + DSAF_SBM_MIB_REQ_FAILED_TC_0_REG + j * 0x80); + p[266 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_CNT_0_REG + j * 0x80); + p[269 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_DROP_CNT_0_REG + j * 0x80); + p[272 + i] = dsaf_read_dev(ddev, + DSAF_SBM_INF_OUTPORT_CNT_0_REG + j * 0x80); + p[275 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC0_CNT_0_REG + j * 0x80); + p[278 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC1_CNT_0_REG + j * 0x80); + p[281 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC2_CNT_0_REG + j * 0x80); + p[284 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC3_CNT_0_REG + j * 0x80); + p[287 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC4_CNT_0_REG + j * 0x80); + p[290 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC5_CNT_0_REG + j * 0x80); + p[293 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC6_CNT_0_REG + j * 0x80); + p[296 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC7_CNT_0_REG + j * 0x80); + p[299 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_REQ_CNT_0_REG + j * 0x80); + p[302 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_RELS_CNT_0_REG + j * 0x80); + p[305 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_3_REG_0_REG + j * 0x80); + p[308 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_4_REG_0_REG + j * 0x80); + } + + /* dsaf onode registers */ + for (i = 0; i < DSAF_XOD_NUM; i++) { + p[311 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_TSA_TC0_TC3_CFG_0_REG + j * 0x90); + p[319 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_TSA_TC4_TC7_CFG_0_REG + j * 0x90); + p[327 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_BW_TC0_TC3_CFG_0_REG + j * 0x90); + p[335 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_BW_TC4_TC7_CFG_0_REG + j * 0x90); + p[343 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_BW_OFFSET_CFG_0_REG + j * 0x90); + p[351 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_TOKEN_CFG_0_REG + j * 0x90); + } + + p[359] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_0_0_REG + port * 0x90); + p[360] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_1_0_REG + port * 0x90); + p[361] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_2_0_REG + port * 0x90); + + for (i = 0; i < DSAF_XOD_BIG_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[362 + i] = dsaf_read_dev(ddev, + DSAF_XOD_GNT_L_0_REG + j * 0x90); + p[365 + i] = dsaf_read_dev(ddev, + DSAF_XOD_GNT_H_0_REG + j * 0x90); + p[368 + i] = dsaf_read_dev(ddev, + DSAF_XOD_CONNECT_STATE_0_REG + j * 0x90); + p[371 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVPKT_CNT_0_REG + j * 0x90); + p[374 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC0_CNT_0_REG + j * 0x90); + p[377 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC1_CNT_0_REG + j * 0x90); + p[380 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC2_CNT_0_REG + j * 0x90); + p[383 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC3_CNT_0_REG + j * 0x90); + p[386 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVVC0_CNT_0_REG + j * 0x90); + p[389 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVVC1_CNT_0_REG + j * 0x90); + } + + p[392] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN0_CNT_0_REG + port * 0x90); + p[393] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN1_CNT_0_REG + port * 0x90); + p[394] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN2_CNT_0_REG + port * 0x90); + p[395] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN3_CNT_0_REG + port * 0x90); + p[396] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN4_CNT_0_REG + port * 0x90); + p[397] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN5_CNT_0_REG + port * 0x90); + p[398] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN6_CNT_0_REG + port * 0x90); + p[399] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN7_CNT_0_REG + port * 0x90); + p[400] = dsaf_read_dev(ddev, + DSAF_XOD_PPE_RCVIN0_CNT_0_REG + port * 0x90); + p[401] = dsaf_read_dev(ddev, + DSAF_XOD_PPE_RCVIN1_CNT_0_REG + port * 0x90); + p[402] = dsaf_read_dev(ddev, + DSAF_XOD_ROCEE_RCVIN0_CNT_0_REG + port * 0x90); + p[403] = dsaf_read_dev(ddev, + DSAF_XOD_ROCEE_RCVIN1_CNT_0_REG + port * 0x90); + p[404] = dsaf_read_dev(ddev, + DSAF_XOD_FIFO_STATUS_0_REG + port * 0x90); + + /* dsaf voq registers */ + for (i = 0; i < DSAF_VOQ_NUM / DSAF_COMM_CHN; i++) { + j = (i * DSAF_COMM_CHN + port) * 0x90; + p[405 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_ECC_INVERT_EN_0_REG + j); + p[408 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_SRAM_PKT_NUM_0_REG + j); + p[411 + i] = dsaf_read_dev(ddev, DSAF_VOQ_IN_PKT_NUM_0_REG + j); + p[414 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_OUT_PKT_NUM_0_REG + j); + p[417 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_ECC_ERR_ADDR_0_REG + j); + p[420 + i] = dsaf_read_dev(ddev, DSAF_VOQ_BP_STATUS_0_REG + j); + p[423 + i] = dsaf_read_dev(ddev, DSAF_VOQ_SPUP_IDLE_0_REG + j); + p[426 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_XGE_XOD_REQ_0_0_REG + j); + p[429 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_XGE_XOD_REQ_1_0_REG + j); + p[432 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_PPE_XOD_REQ_0_REG + j); + p[435 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_ROCEE_XOD_REQ_0_REG + j); + p[438 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_BP_ALL_THRD_0_REG + j); + } + + /* dsaf tbl registers */ + p[441] = dsaf_read_dev(ddev, DSAF_TBL_CTRL_0_REG); + p[442] = dsaf_read_dev(ddev, DSAF_TBL_INT_MSK_0_REG); + p[443] = dsaf_read_dev(ddev, DSAF_TBL_INT_SRC_0_REG); + p[444] = dsaf_read_dev(ddev, DSAF_TBL_INT_STS_0_REG); + p[445] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_ADDR_0_REG); + p[446] = dsaf_read_dev(ddev, DSAF_TBL_LINE_ADDR_0_REG); + p[447] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_HIGH_0_REG); + p[448] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_LOW_0_REG); + p[449] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG); + p[450] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG); + p[451] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG); + p[452] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG); + p[453] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG); + p[454] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_UCAST_CFG_0_REG); + p[455] = dsaf_read_dev(ddev, DSAF_TBL_LIN_CFG_0_REG); + p[456] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG); + p[457] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_LOW_0_REG); + p[458] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA4_0_REG); + p[459] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA3_0_REG); + p[460] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA2_0_REG); + p[461] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA1_0_REG); + p[462] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA0_0_REG); + p[463] = dsaf_read_dev(ddev, DSAF_TBL_LIN_RDATA_0_REG); + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) { + j = i * 0x8; + p[464 + 2 * i] = dsaf_read_dev(ddev, + DSAF_TBL_DA0_MIS_INFO1_0_REG + j); + p[465 + 2 * i] = dsaf_read_dev(ddev, + DSAF_TBL_DA0_MIS_INFO0_0_REG + j); + } + + p[480] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO2_0_REG); + p[481] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO1_0_REG); + p[482] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO0_0_REG); + p[483] = dsaf_read_dev(ddev, DSAF_TBL_PUL_0_REG); + p[484] = dsaf_read_dev(ddev, DSAF_TBL_OLD_RSLT_0_REG); + p[485] = dsaf_read_dev(ddev, DSAF_TBL_OLD_SCAN_VAL_0_REG); + p[486] = dsaf_read_dev(ddev, DSAF_TBL_DFX_CTRL_0_REG); + p[487] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_0_REG); + p[488] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_2_0_REG); + p[489] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_I_0_REG); + p[490] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_O_0_REG); + p[491] = dsaf_read_dev(ddev, DSAF_TBL_UCAST_BCAST_MIS_INFO_0_0_REG); + + /* dsaf other registers */ + p[492] = dsaf_read_dev(ddev, DSAF_INODE_FIFO_WL_0_REG + port * 0x4); + p[493] = dsaf_read_dev(ddev, DSAF_ONODE_FIFO_WL_0_REG + port * 0x4); + p[494] = dsaf_read_dev(ddev, DSAF_XGE_GE_WORK_MODE_0_REG + port * 0x4); + p[495] = dsaf_read_dev(ddev, + DSAF_XGE_APP_RX_LINK_UP_0_REG + port * 0x4); + p[496] = dsaf_read_dev(ddev, DSAF_NETPORT_CTRL_SIG_0_REG + port * 0x4); + p[497] = dsaf_read_dev(ddev, DSAF_XGE_CTRL_SIG_CFG_0_REG + port * 0x4); + + /* mark end of dsaf regs */ + for (i = 498; i < 504; i++) + p[i] = 0xdddddddd; +} + +static char *hns_dsaf_get_node_stats_strings(char *data, int node) +{ + char *buff = data; + + snprintf(buff, ETH_GSTRING_LEN, "innod%d_pad_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_manage_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pkt_id", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pause_frame", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_release_buf_num", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_sbm_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_crc_false_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_bp_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_lookup_rslt_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_local_rslt_fail_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_vlan_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_stp_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "onnod%d_tx_pkts", node); + buff = buff + ETH_GSTRING_LEN; + + return buff; +} + +static u64 *hns_dsaf_get_node_stats(struct dsaf_device *ddev, u64 *data, + int node_num) +{ + u64 *p = data; + struct dsaf_hw_stats *hw_stats = &ddev->hw_stats[node_num]; + + p[0] = hw_stats->pad_drop; + p[1] = hw_stats->man_pkts; + p[2] = hw_stats->rx_pkts; + p[3] = hw_stats->rx_pkt_id; + p[4] = hw_stats->rx_pause_frame; + p[5] = hw_stats->release_buf_num; + p[6] = hw_stats->sbm_drop; + p[7] = hw_stats->crc_false; + p[8] = hw_stats->bp_drop; + p[9] = hw_stats->rslt_drop; + p[10] = hw_stats->local_addr_false; + p[11] = hw_stats->vlan_drop; + p[12] = hw_stats->stp_drop; + p[13] = hw_stats->tx_pkts; + + return &p[14]; +} + +/** + *hns_dsaf_get_stats - get dsaf statistic + *@ddev: dsaf device + *@data:statistic value + *@port: port num + */ +void hns_dsaf_get_stats(struct dsaf_device *ddev, u64 *data, int port) +{ + u64 *p = data; + int node_num = port; + + /* for ge/xge node info */ + p = hns_dsaf_get_node_stats(ddev, p, node_num); + + /* for ppe node info */ + node_num = port + DSAF_PPE_INODE_BASE; + (void)hns_dsaf_get_node_stats(ddev, p, node_num); +} + +/** + *hns_dsaf_get_sset_count - get dsaf string set count + *@stringset: type of values in data + *return dsaf string name count + */ +int hns_dsaf_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return DSAF_STATIC_NUM; + + return 0; +} + +/** + *hns_dsaf_get_strings - get dsaf string set + *@stringset:srting set index + *@data:strings name value + *@port:port index + */ +void hns_dsaf_get_strings(int stringset, u8 *data, int port) +{ + char *buff = (char *)data; + int node = port; + + if (stringset != ETH_SS_STATS) + return; + + /* for ge/xge node info */ + buff = hns_dsaf_get_node_stats_strings(buff, node); + + /* for ppe node info */ + node = port + DSAF_PPE_INODE_BASE; + (void)hns_dsaf_get_node_stats_strings(buff, node); +} + +/** + *hns_dsaf_get_sset_count - get dsaf regs count + *return dsaf regs count + */ +int hns_dsaf_get_regs_count(void) +{ + return DSAF_DUMP_REGS_NUM; +} + +/** + * dsaf_probe - probo dsaf dev + * @pdev: dasf platform device + * retuen 0 - success , negative --fail + */ +static int hns_dsaf_probe(struct platform_device *pdev) +{ + struct dsaf_device *dsaf_dev; + int ret; + + dsaf_dev = hns_dsaf_alloc_dev(&pdev->dev, sizeof(struct dsaf_drv_priv)); + if (IS_ERR(dsaf_dev)) { + ret = PTR_ERR(dsaf_dev); + dev_err(&pdev->dev, + "dsaf_probe dsaf_alloc_dev failed, ret = %#x!\n", ret); + return ret; + } + + ret = hns_dsaf_get_cfg(dsaf_dev); + if (ret) + goto free_dev; + + ret = hns_dsaf_init(dsaf_dev); + if (ret) + goto free_cfg; + + ret = hns_mac_init(dsaf_dev); + if (ret) + goto uninit_dsaf; + + ret = hns_ppe_init(dsaf_dev); + if (ret) + goto uninit_mac; + + ret = hns_dsaf_ae_init(dsaf_dev); + if (ret) + goto uninit_ppe; + + return 0; + +uninit_ppe: + hns_ppe_uninit(dsaf_dev); + +uninit_mac: + hns_mac_uninit(dsaf_dev); + +uninit_dsaf: + hns_dsaf_free(dsaf_dev); + +free_cfg: + hns_dsaf_free_cfg(dsaf_dev); + +free_dev: + hns_dsaf_free_dev(dsaf_dev); + + return ret; +} + +/** + * dsaf_remove - remove dsaf dev + * @pdev: dasf platform device + */ +static int hns_dsaf_remove(struct platform_device *pdev) +{ + struct dsaf_device *dsaf_dev = dev_get_drvdata(&pdev->dev); + + hns_dsaf_ae_uninit(dsaf_dev); + + hns_ppe_uninit(dsaf_dev); + + hns_mac_uninit(dsaf_dev); + + hns_dsaf_free(dsaf_dev); + + hns_dsaf_free_cfg(dsaf_dev); + + hns_dsaf_free_dev(dsaf_dev); + + return 0; +} + +static const struct of_device_id g_dsaf_match[] = { + {.compatible = "hisilicon,hns-dsaf-v1"}, + {.compatible = "hisilicon,hns-dsaf-v2"}, + {} +}; + +static struct platform_driver g_dsaf_driver = { + .probe = hns_dsaf_probe, + .remove = hns_dsaf_remove, + .driver = { + .name = DSAF_DRV_NAME, + .of_match_table = g_dsaf_match, + }, +}; + +module_platform_driver(g_dsaf_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_DESCRIPTION("HNS DSAF driver"); +MODULE_VERSION(DSAF_MOD_VERSION); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h new file mode 100644 index 000000000000..315b07ecd291 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HNS_DSAF_MAIN_H +#define __HNS_DSAF_MAIN_H +#include "hnae.h" + +#include "hns_dsaf_reg.h" +#include "hns_dsaf_mac.h" + +struct hns_mac_cb; + +#define DSAF_DRV_NAME "hns_dsaf" +#define DSAF_MOD_VERSION "v1.0" + +#define ENABLE (0x1) +#define DISABLE (0x0) + +#define HNS_DSAF_DEBUG_NW_REG_OFFSET (0x100000) + +#define DSAF_BASE_INNER_PORT_NUM (127) /* mac tbl qid*/ + +#define DSAF_MAX_CHIP_NUM (2) /*max 2 chips */ + +#define DSAF_DEFAUTL_QUEUE_NUM_PER_PPE (22) + +#define HNS_DSAF_MAX_DESC_CNT (1024) +#define HNS_DSAF_MIN_DESC_CNT (16) + +#define DSAF_INVALID_ENTRY_IDX (0xffff) + +#define DSAF_CFG_READ_CNT (30) +#define DSAF_SRAM_INIT_FINISH_FLAG (0xff) + +#define MAC_NUM_OCTETS_PER_ADDR 6 + +#define DSAF_DUMP_REGS_NUM 504 +#define DSAF_STATIC_NUM 28 + +#define DSAF_STATS_READ(p, offset) (*((u64 *)((u8 *)(p) + (offset)))) + +enum hal_dsaf_mode { + HRD_DSAF_NO_DSAF_MODE = 0x0, + HRD_DSAF_MODE = 0x1, +}; + +enum hal_dsaf_tc_mode { + HRD_DSAF_4TC_MODE = 0X0, + HRD_DSAF_8TC_MODE = 0X1, +}; + +struct dsaf_vm_def_vlan { + u32 vm_def_vlan_id; + u32 vm_def_vlan_cfi; + u32 vm_def_vlan_pri; +}; + +struct dsaf_tbl_tcam_data { + u32 tbl_tcam_data_high; + u32 tbl_tcam_data_low; +}; + +#define DSAF_PORT_MSK_NUM \ + ((DSAF_TOTAL_QUEUE_NUM + DSAF_SERVICE_NW_NUM - 1) / 32 + 1) +struct dsaf_tbl_tcam_mcast_cfg { + u8 tbl_mcast_old_en; + u8 tbl_mcast_item_vld; + u32 tbl_mcast_port_msk[DSAF_PORT_MSK_NUM]; +}; + +struct dsaf_tbl_tcam_ucast_cfg { + u32 tbl_ucast_old_en; + u32 tbl_ucast_item_vld; + u32 tbl_ucast_mac_discard; + u32 tbl_ucast_dvc; + u32 tbl_ucast_out_port; +}; + +struct dsaf_tbl_line_cfg { + u32 tbl_line_mac_discard; + u32 tbl_line_dvc; + u32 tbl_line_out_port; +}; + +enum dsaf_port_rate_mode { + DSAF_PORT_RATE_1000 = 0, + DSAF_PORT_RATE_2500, + DSAF_PORT_RATE_10000 +}; + +enum dsaf_stp_port_type { + DSAF_STP_PORT_TYPE_DISCARD = 0, + DSAF_STP_PORT_TYPE_BLOCK = 1, + DSAF_STP_PORT_TYPE_LISTEN = 2, + DSAF_STP_PORT_TYPE_LEARN = 3, + DSAF_STP_PORT_TYPE_FORWARD = 4 +}; + +enum dsaf_sw_port_type { + DSAF_SW_PORT_TYPE_NON_VLAN = 0, + DSAF_SW_PORT_TYPE_ACCESS = 1, + DSAF_SW_PORT_TYPE_TRUNK = 2, +}; + +#define DSAF_SUB_BASE_SIZE (0x10000) + +/* dsaf mode define */ +enum dsaf_mode { + DSAF_MODE_INVALID = 0, /**< Invalid dsaf mode */ + DSAF_MODE_ENABLE_FIX, /**< en DSAF-mode, fixed to queue*/ + DSAF_MODE_ENABLE_0VM, /**< en DSAF-mode, support 0 VM */ + DSAF_MODE_ENABLE_8VM, /**< en DSAF-mode, support 8 VM */ + DSAF_MODE_ENABLE_16VM, /**< en DSAF-mode, support 16 VM */ + DSAF_MODE_ENABLE_32VM, /**< en DSAF-mode, support 32 VM */ + DSAF_MODE_ENABLE_128VM, /**< en DSAF-mode, support 128 VM */ + DSAF_MODE_ENABLE, /**< before is enable DSAF mode*/ + DSAF_MODE_DISABLE_FIX, /**< non-dasf, fixed to queue*/ + DSAF_MODE_DISABLE_2PORT_8VM, /**< non-dasf, 2port 8VM */ + DSAF_MODE_DISABLE_2PORT_16VM, /**< non-dasf, 2port 16VM */ + DSAF_MODE_DISABLE_2PORT_64VM, /**< non-dasf, 2port 64VM */ + DSAF_MODE_DISABLE_6PORT_0VM, /**< non-dasf, 6port 0VM */ + DSAF_MODE_DISABLE_6PORT_2VM, /**< non-dasf, 6port 2VM */ + DSAF_MODE_DISABLE_6PORT_4VM, /**< non-dasf, 6port 4VM */ + DSAF_MODE_DISABLE_6PORT_16VM, /**< non-dasf, 6port 16VM */ + DSAF_MODE_MAX /**< the last one, use as the num */ +}; + +#define DSAF_DEST_PORT_NUM 256 /* DSAF max port num */ +#define DSAF_WORD_BIT_CNT 32 /* the num bit of word */ + +/*mac entry, mc or uc entry*/ +struct dsaf_drv_mac_single_dest_entry { + /* mac addr, match the entry*/ + u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u16 in_vlan_id; /* value of VlanId */ + + /* the vld input port num, dsaf-mode fix 0, */ + /* non-dasf is the entry whitch port vld*/ + u8 in_port_num; + + u8 port_num; /*output port num*/ + u8 rsv[6]; +}; + +/*only mc entry*/ +struct dsaf_drv_mac_multi_dest_entry { + /* mac addr, match the entry*/ + u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u16 in_vlan_id; + /* this mac addr output port,*/ + /* bit0-bit5 means Port0-Port5(1bit is vld)**/ + u32 port_mask[DSAF_DEST_PORT_NUM / DSAF_WORD_BIT_CNT]; + + /* the vld input port num, dsaf-mode fix 0,*/ + /* non-dasf is the entry whitch port vld*/ + u8 in_port_num; + u8 rsv[7]; +}; + +struct dsaf_hw_stats { + u64 pad_drop; + u64 man_pkts; + u64 rx_pkts; + u64 rx_pkt_id; + u64 rx_pause_frame; + u64 release_buf_num; + u64 sbm_drop; + u64 crc_false; + u64 bp_drop; + u64 rslt_drop; + u64 local_addr_false; + u64 vlan_drop; + u64 stp_drop; + u64 tx_pkts; +}; + +struct hnae_vf_cb { + u8 port_index; + struct hns_mac_cb *mac_cb; + struct dsaf_device *dsaf_dev; + struct hnae_handle ae_handle; /* must be the last number */ +}; + +struct dsaf_int_xge_src { + u32 xid_xge_ecc_err_int_src; + u32 xid_xge_fsm_timout_int_src; + u32 sbm_xge_lnk_fsm_timout_int_src; + u32 sbm_xge_lnk_ecc_2bit_int_src; + u32 sbm_xge_mib_req_failed_int_src; + u32 sbm_xge_mib_req_fsm_timout_int_src; + u32 sbm_xge_mib_rels_fsm_timout_int_src; + u32 sbm_xge_sram_ecc_2bit_int_src; + u32 sbm_xge_mib_buf_sum_err_int_src; + u32 sbm_xge_mib_req_extra_int_src; + u32 sbm_xge_mib_rels_extra_int_src; + u32 voq_xge_start_to_over_0_int_src; + u32 voq_xge_start_to_over_1_int_src; + u32 voq_xge_ecc_err_int_src; +}; + +struct dsaf_int_ppe_src { + u32 xid_ppe_fsm_timout_int_src; + u32 sbm_ppe_lnk_fsm_timout_int_src; + u32 sbm_ppe_lnk_ecc_2bit_int_src; + u32 sbm_ppe_mib_req_failed_int_src; + u32 sbm_ppe_mib_req_fsm_timout_int_src; + u32 sbm_ppe_mib_rels_fsm_timout_int_src; + u32 sbm_ppe_sram_ecc_2bit_int_src; + u32 sbm_ppe_mib_buf_sum_err_int_src; + u32 sbm_ppe_mib_req_extra_int_src; + u32 sbm_ppe_mib_rels_extra_int_src; + u32 voq_ppe_start_to_over_0_int_src; + u32 voq_ppe_ecc_err_int_src; + u32 xod_ppe_fifo_rd_empty_int_src; + u32 xod_ppe_fifo_wr_full_int_src; +}; + +struct dsaf_int_rocee_src { + u32 xid_rocee_fsm_timout_int_src; + u32 sbm_rocee_lnk_fsm_timout_int_src; + u32 sbm_rocee_lnk_ecc_2bit_int_src; + u32 sbm_rocee_mib_req_failed_int_src; + u32 sbm_rocee_mib_req_fsm_timout_int_src; + u32 sbm_rocee_mib_rels_fsm_timout_int_src; + u32 sbm_rocee_sram_ecc_2bit_int_src; + u32 sbm_rocee_mib_buf_sum_err_int_src; + u32 sbm_rocee_mib_req_extra_int_src; + u32 sbm_rocee_mib_rels_extra_int_src; + u32 voq_rocee_start_to_over_0_int_src; + u32 voq_rocee_ecc_err_int_src; +}; + +struct dsaf_int_tbl_src { + u32 tbl_da0_mis_src; + u32 tbl_da1_mis_src; + u32 tbl_da2_mis_src; + u32 tbl_da3_mis_src; + u32 tbl_da4_mis_src; + u32 tbl_da5_mis_src; + u32 tbl_da6_mis_src; + u32 tbl_da7_mis_src; + u32 tbl_sa_mis_src; + u32 tbl_old_sech_end_src; + u32 lram_ecc_err1_src; + u32 lram_ecc_err2_src; + u32 tram_ecc_err1_src; + u32 tram_ecc_err2_src; + u32 tbl_ucast_bcast_xge0_src; + u32 tbl_ucast_bcast_xge1_src; + u32 tbl_ucast_bcast_xge2_src; + u32 tbl_ucast_bcast_xge3_src; + u32 tbl_ucast_bcast_xge4_src; + u32 tbl_ucast_bcast_xge5_src; + u32 tbl_ucast_bcast_ppe_src; + u32 tbl_ucast_bcast_rocee_src; +}; + +struct dsaf_int_stat { + struct dsaf_int_xge_src dsaf_int_xge_stat[DSAF_COMM_CHN]; + struct dsaf_int_ppe_src dsaf_int_ppe_stat[DSAF_COMM_CHN]; + struct dsaf_int_rocee_src dsaf_int_rocee_stat[DSAF_COMM_CHN]; + struct dsaf_int_tbl_src dsaf_int_tbl_stat[1]; + +}; + +/* Dsaf device struct define ,and mac -> dsaf */ +struct dsaf_device { + struct device *dev; + struct hnae_ae_dev ae_dev; + + void *priv; + + int virq[DSAF_IRQ_NUM]; + + u8 __iomem *sc_base; + u8 __iomem *sds_base; + u8 __iomem *ppe_base; + u8 __iomem *io_base; + u8 __iomem *cpld_base; + + u32 desc_num; /* desc num per queue*/ + u32 buf_size; /* ring buffer size */ + int buf_size_type; /* ring buffer size-type */ + enum dsaf_mode dsaf_mode; /* dsaf mode */ + enum hal_dsaf_mode dsaf_en; + enum hal_dsaf_tc_mode dsaf_tc_mode; + u32 dsaf_ver; + + struct ppe_common_cb *ppe_common[DSAF_COMM_DEV_NUM]; + struct rcb_common_cb *rcb_common[DSAF_COMM_DEV_NUM]; + struct hns_mac_cb *mac_cb; + + struct dsaf_hw_stats hw_stats[DSAF_NODE_NUM]; + struct dsaf_int_stat int_stat; +}; + +static inline void *hns_dsaf_dev_priv(const struct dsaf_device *dsaf_dev) +{ + return (void *)((u8 *)dsaf_dev + sizeof(*dsaf_dev)); +} + +struct dsaf_drv_tbl_tcam_key { + union { + struct { + u8 mac_3; + u8 mac_2; + u8 mac_1; + u8 mac_0; + } bits; + + u32 val; + } high; + union { + struct { + u32 port:4; /* port id, */ + /* dsaf-mode fixed 0, non-dsaf-mode port id*/ + u32 vlan:12; /* vlan id */ + u32 mac_5:8; + u32 mac_4:8; + } bits; + + u32 val; + } low; +}; + +struct dsaf_drv_soft_mac_tbl { + struct dsaf_drv_tbl_tcam_key tcam_key; + u16 index; /*the entry's index in tcam tab*/ +}; + +struct dsaf_drv_priv { + /* soft tab Mac key, for hardware tab*/ + struct dsaf_drv_soft_mac_tbl *soft_mac_tbl; +}; + +static inline void hns_dsaf_tbl_tcam_addr_cfg(struct dsaf_device *dsaf_dev, + u32 tab_tcam_addr) +{ + dsaf_set_dev_field(dsaf_dev, DSAF_TBL_TCAM_ADDR_0_REG, + DSAF_TBL_TCAM_ADDR_M, DSAF_TBL_TCAM_ADDR_S, + tab_tcam_addr); +} + +static inline void hns_dsaf_tbl_tcam_load_pul(struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_LOAD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_LOAD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +static inline void hns_dsaf_tbl_line_addr_cfg(struct dsaf_device *dsaf_dev, + u32 tab_line_addr) +{ + dsaf_set_dev_field(dsaf_dev, DSAF_TBL_LINE_ADDR_0_REG, + DSAF_TBL_LINE_ADDR_M, DSAF_TBL_LINE_ADDR_S, + tab_line_addr); +} + +static inline int hns_dsaf_get_comm_idx_by_port(int port) +{ + if ((port < DSAF_COMM_CHN) || (port == DSAF_MAX_PORT_NUM_PER_CHIP)) + return 0; + else + return (port - DSAF_COMM_CHN + 1); +} + +static inline struct hnae_vf_cb *hns_ae_get_vf_cb( + struct hnae_handle *handle) +{ + return container_of(handle, struct hnae_vf_cb, ae_handle); +} + +int hns_dsaf_set_mac_uc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_set_mac_mc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry); +int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_del_mac_entry(struct dsaf_device *dsaf_dev, u16 vlan_id, + u8 in_port_num, u8 *addr); +int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry); +int hns_dsaf_get_mac_entry_by_index( + struct dsaf_device *dsaf_dev, + u16 entry_index, + struct dsaf_drv_mac_multi_dest_entry *mac_entry); + +void hns_dsaf_rst(struct dsaf_device *dsaf_dev, u32 val); + +void hns_ppe_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val); + +void hns_ppe_com_srst(struct ppe_common_cb *ppe_common, u32 val); + +void hns_dsaf_fix_mac_mode(struct hns_mac_cb *mac_cb); + +int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev); +void hns_dsaf_ae_uninit(struct dsaf_device *dsaf_dev); + +void hns_dsaf_xge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val); +void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val); +void hns_dsaf_xge_core_srst_by_port(struct dsaf_device *dsaf_dev, + u32 port, u32 val); + +void hns_dsaf_update_stats(struct dsaf_device *dsaf_dev, u32 inode_num); + +int hns_dsaf_get_sset_count(int stringset); +void hns_dsaf_get_stats(struct dsaf_device *ddev, u64 *data, int port); +void hns_dsaf_get_strings(int stringset, u8 *data, int port); + +void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data); +int hns_dsaf_get_regs_count(void); + +#endif /* __HNS_DSAF_MAIN_H__ */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c new file mode 100644 index 000000000000..d611388aecef --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "hns_dsaf_misc.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_reg.h" +#include "hns_dsaf_ppe.h" + +void hns_cpld_set_led(struct hns_mac_cb *mac_cb, int link_status, + u16 speed, int data) +{ + int speed_reg = 0; + u8 value; + + if (!mac_cb) { + pr_err("sfp_led_opt mac_dev is null!\n"); + return; + } + if (!mac_cb->cpld_vaddr) { + dev_err(mac_cb->dev, "mac_id=%d, cpld_vaddr is null !\n", + mac_cb->mac_id); + return; + } + + if (speed == MAC_SPEED_10000) + speed_reg = 1; + + value = mac_cb->cpld_led_value; + + if (link_status) { + dsaf_set_bit(value, DSAF_LED_LINK_B, link_status); + dsaf_set_field(value, DSAF_LED_SPEED_M, + DSAF_LED_SPEED_S, speed_reg); + dsaf_set_bit(value, DSAF_LED_DATA_B, data); + + if (value != mac_cb->cpld_led_value) { + dsaf_write_b(mac_cb->cpld_vaddr, value); + mac_cb->cpld_led_value = value; + } + } else { + dsaf_write_b(mac_cb->cpld_vaddr, CPLD_LED_DEFAULT_VALUE); + mac_cb->cpld_led_value = CPLD_LED_DEFAULT_VALUE; + } +} + +void cpld_led_reset(struct hns_mac_cb *mac_cb) +{ + if (!mac_cb || !mac_cb->cpld_vaddr) + return; + + dsaf_write_b(mac_cb->cpld_vaddr, CPLD_LED_DEFAULT_VALUE); + mac_cb->cpld_led_value = CPLD_LED_DEFAULT_VALUE; +} + +int cpld_set_led_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status) +{ + switch (status) { + case HNAE_LED_ACTIVE: + mac_cb->cpld_led_value = dsaf_read_b(mac_cb->cpld_vaddr); + return 2; + case HNAE_LED_ON: + dsaf_set_bit(mac_cb->cpld_led_value, DSAF_LED_ANCHOR_B, + CPLD_LED_ON_VALUE); + dsaf_write_b(mac_cb->cpld_vaddr, mac_cb->cpld_led_value); + break; + case HNAE_LED_OFF: + dsaf_set_bit(mac_cb->cpld_led_value, DSAF_LED_ANCHOR_B, + CPLD_LED_DEFAULT_VALUE); + dsaf_write_b(mac_cb->cpld_vaddr, mac_cb->cpld_led_value); + break; + case HNAE_LED_INACTIVE: + dsaf_set_bit(mac_cb->cpld_led_value, DSAF_LED_ANCHOR_B, + CPLD_LED_DEFAULT_VALUE); + dsaf_write_b(mac_cb->cpld_vaddr, mac_cb->cpld_led_value); + break; + default: + break; + } + + return 0; +} + +#define RESET_REQ_OR_DREQ 1 + +void hns_dsaf_rst(struct dsaf_device *dsaf_dev, u32 val) +{ + u32 xbar_reg_addr; + u32 nt_reg_addr; + + if (!val) { + xbar_reg_addr = DSAF_SUB_SC_XBAR_RESET_REQ_REG; + nt_reg_addr = DSAF_SUB_SC_NT_RESET_REQ_REG; + } else { + xbar_reg_addr = DSAF_SUB_SC_XBAR_RESET_DREQ_REG; + nt_reg_addr = DSAF_SUB_SC_NT_RESET_DREQ_REG; + } + + dsaf_write_reg(dsaf_dev->sc_base, xbar_reg_addr, + RESET_REQ_OR_DREQ); + dsaf_write_reg(dsaf_dev->sc_base, nt_reg_addr, + RESET_REQ_OR_DREQ); +} + +void hns_dsaf_xge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val) +{ + u32 reg_val = 0; + u32 reg_addr; + + if (port >= DSAF_XGE_NUM) + return; + + reg_val |= RESET_REQ_OR_DREQ; + reg_val |= 0x2082082 << port; + + if (val == 0) + reg_addr = DSAF_SUB_SC_XGE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_XGE_RESET_DREQ_REG; + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +void hns_dsaf_xge_core_srst_by_port(struct dsaf_device *dsaf_dev, + u32 port, u32 val) +{ + u32 reg_val = 0; + u32 reg_addr; + + if (port >= DSAF_XGE_NUM) + return; + + reg_val |= XGMAC_TRX_CORE_SRST_M << port; + + if (val == 0) + reg_addr = DSAF_SUB_SC_XGE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_XGE_RESET_DREQ_REG; + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val) +{ + u32 reg_val_1; + u32 reg_val_2; + + if (port >= DSAF_GE_NUM) + return; + + if (port < DSAF_SERVICE_NW_NUM) { + reg_val_1 = 0x1 << port; + reg_val_2 = 0x1041041 << port; + + if (val == 0) { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_REQ1_REG, + reg_val_1); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_REQ0_REG, + reg_val_2); + } else { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_DREQ0_REG, + reg_val_2); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_DREQ1_REG, + reg_val_1); + } + } else { + reg_val_1 = 0x15540 << (port - 6); + reg_val_2 = 0x100 << (port - 6); + + if (val == 0) { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_REQ1_REG, + reg_val_1); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_PPE_RESET_REQ_REG, + reg_val_2); + } else { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_DREQ1_REG, + reg_val_1); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_PPE_RESET_DREQ_REG, + reg_val_2); + } + } +} + +void hns_ppe_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val) +{ + u32 reg_val = 0; + u32 reg_addr; + + reg_val |= RESET_REQ_OR_DREQ << port; + + if (val == 0) + reg_addr = DSAF_SUB_SC_PPE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_PPE_RESET_DREQ_REG; + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +void hns_ppe_com_srst(struct ppe_common_cb *ppe_common, u32 val) +{ + int comm_index = ppe_common->comm_index; + struct dsaf_device *dsaf_dev = ppe_common->dsaf_dev; + u32 reg_val; + u32 reg_addr; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + reg_val = RESET_REQ_OR_DREQ; + if (val == 0) + reg_addr = DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG; + + } else { + reg_val = 0x100 << (comm_index - 1); + + if (val == 0) + reg_addr = DSAF_SUB_SC_PPE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_PPE_RESET_DREQ_REG; + } + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +/** + * hns_mac_get_sds_mode - get phy ifterface form serdes mode + * @mac_cb: mac control block + * retuen phy interface + */ +phy_interface_t hns_mac_get_phy_if(struct hns_mac_cb *mac_cb) +{ + u32 hilink3_mode; + u32 hilink4_mode; + void __iomem *sys_ctl_vaddr = mac_cb->sys_ctl_vaddr; + int dev_id = mac_cb->mac_id; + phy_interface_t phy_if = PHY_INTERFACE_MODE_NA; + + hilink3_mode = dsaf_read_reg(sys_ctl_vaddr, HNS_MAC_HILINK3_REG); + hilink4_mode = dsaf_read_reg(sys_ctl_vaddr, HNS_MAC_HILINK4_REG); + if (dev_id >= 0 && dev_id <= 3) { + if (hilink4_mode == 0) + phy_if = PHY_INTERFACE_MODE_SGMII; + else + phy_if = PHY_INTERFACE_MODE_XGMII; + } else if (dev_id >= 4 && dev_id <= 5) { + if (hilink3_mode == 0) + phy_if = PHY_INTERFACE_MODE_SGMII; + else + phy_if = PHY_INTERFACE_MODE_XGMII; + } else { + phy_if = PHY_INTERFACE_MODE_SGMII; + } + + dev_dbg(mac_cb->dev, + "hilink3_mode=%d, hilink4_mode=%d dev_id=%d, phy_if=%d\n", + hilink3_mode, hilink4_mode, dev_id, phy_if); + return phy_if; +} + +/** + * hns_mac_config_sds_loopback - set loop back for serdes + * @mac_cb: mac control block + * retuen 0 == success + */ +int hns_mac_config_sds_loopback(struct hns_mac_cb *mac_cb, u8 en) +{ + /* port 0-3 hilink4 base is serdes_vaddr + 0x00280000 + * port 4-7 hilink3 base is serdes_vaddr + 0x00200000 + */ + u8 *base_addr = (u8 *)mac_cb->serdes_vaddr + + (mac_cb->mac_id <= 3 ? 0x00280000 : 0x00200000); + const u8 lane_id[] = { + 0, /* mac 0 -> lane 0 */ + 1, /* mac 1 -> lane 1 */ + 2, /* mac 2 -> lane 2 */ + 3, /* mac 3 -> lane 3 */ + 2, /* mac 4 -> lane 2 */ + 3, /* mac 5 -> lane 3 */ + 0, /* mac 6 -> lane 0 */ + 1 /* mac 7 -> lane 1 */ + }; +#define RX_CSR(lane, reg) ((0x4080 + (reg) * 0x0002 + (lane) * 0x0200) * 2) + u64 reg_offset = RX_CSR(lane_id[mac_cb->mac_id], 0); + + int sfp_prsnt; + int ret = hns_mac_get_sfp_prsnt(mac_cb, &sfp_prsnt); + + if (!mac_cb->phy_node) { + if (ret) + pr_info("please confirm sfp is present or not\n"); + else + if (!sfp_prsnt) + pr_info("no sfp in this eth\n"); + } + + dsaf_set_reg_field(base_addr, reg_offset, 1ull << 10, 10, !!en); + + return 0; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h new file mode 100644 index 000000000000..419f07aa9734 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_DSAF_MISC_H +#define _HNS_DSAF_MISC_H + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> + +#include "hns_dsaf_mac.h" + +#define CPLD_ADDR_PORT_OFFSET 0x4 + +#define HS_LED_ON 0xE +#define HS_LED_OFF 0xF + +#define CPLD_LED_ON_VALUE 1 +#define CPLD_LED_DEFAULT_VALUE 0 + +#define MAC_SFP_PORT_OFFSET 0x2 + +#define DSAF_LED_SPEED_S 0 +#define DSAF_LED_SPEED_M (0x3 << DSAF_LED_SPEED_S) + +#define DSAF_LED_LINK_B 2 +#define DSAF_LED_DATA_B 4 +#define DSAF_LED_ANCHOR_B 5 + +void hns_cpld_set_led(struct hns_mac_cb *mac_cb, int link_status, + u16 speed, int data); +void cpld_led_reset(struct hns_mac_cb *mac_cb); +int cpld_set_led_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status); +int hns_mac_get_sfp_prsnt(struct hns_mac_cb *mac_cb, int *sfp_prsnt); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c new file mode 100644 index 000000000000..67f33f185a44 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +#include "hns_dsaf_ppe.h" + +static void __iomem *hns_ppe_common_get_ioaddr( + struct ppe_common_cb *ppe_common) +{ + void __iomem *base_addr; + + int idx = ppe_common->comm_index; + + if (idx == HNS_DSAF_COMM_SERVICE_NW_IDX) + base_addr = ppe_common->dsaf_dev->ppe_base + + PPE_COMMON_REG_OFFSET; + else + base_addr = ppe_common->dsaf_dev->sds_base + + (idx - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET + + PPE_COMMON_REG_OFFSET; + + return base_addr; +} + +/** + * hns_ppe_common_get_cfg - get ppe common config + * @dsaf_dev: dasf device + * comm_index: common index + * retuen 0 - success , negative --fail + */ +int hns_ppe_common_get_cfg(struct dsaf_device *dsaf_dev, int comm_index) +{ + struct ppe_common_cb *ppe_common; + int ppe_num; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + ppe_num = HNS_PPE_SERVICE_NW_ENGINE_NUM; + else + ppe_num = HNS_PPE_DEBUG_NW_ENGINE_NUM; + + ppe_common = devm_kzalloc(dsaf_dev->dev, sizeof(*ppe_common) + + ppe_num * sizeof(struct hns_ppe_cb), GFP_KERNEL); + if (!ppe_common) + return -ENOMEM; + + ppe_common->ppe_num = ppe_num; + ppe_common->dsaf_dev = dsaf_dev; + ppe_common->comm_index = comm_index; + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + ppe_common->ppe_mode = PPE_COMMON_MODE_SERVICE; + else + ppe_common->ppe_mode = PPE_COMMON_MODE_DEBUG; + ppe_common->dev = dsaf_dev->dev; + + ppe_common->io_base = hns_ppe_common_get_ioaddr(ppe_common); + + dsaf_dev->ppe_common[comm_index] = ppe_common; + + return 0; +} + +void hns_ppe_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index) +{ + dsaf_dev->ppe_common[comm_index] = NULL; +} + +static void __iomem *hns_ppe_get_iobase(struct ppe_common_cb *ppe_common, + int ppe_idx) +{ + void __iomem *base_addr; + int common_idx = ppe_common->comm_index; + + if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE) { + base_addr = ppe_common->dsaf_dev->ppe_base + + ppe_idx * PPE_REG_OFFSET; + + } else { + base_addr = ppe_common->dsaf_dev->sds_base + + (common_idx - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET; + } + + return base_addr; +} + +static int hns_ppe_get_port(struct ppe_common_cb *ppe_common, int idx) +{ + int port; + + if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE) + port = idx; + else + port = HNS_PPE_SERVICE_NW_ENGINE_NUM + + ppe_common->comm_index - 1; + + return port; +} + +static void hns_ppe_get_cfg(struct ppe_common_cb *ppe_common) +{ + u32 i; + struct hns_ppe_cb *ppe_cb; + u32 ppe_num = ppe_common->ppe_num; + + for (i = 0; i < ppe_num; i++) { + ppe_cb = &ppe_common->ppe_cb[i]; + ppe_cb->dev = ppe_common->dev; + ppe_cb->next = NULL; + ppe_cb->ppe_common_cb = ppe_common; + ppe_cb->index = i; + ppe_cb->port = hns_ppe_get_port(ppe_common, i); + ppe_cb->io_base = hns_ppe_get_iobase(ppe_common, i); + ppe_cb->virq = 0; + } +} + +static void hns_ppe_cnt_clr_ce(struct hns_ppe_cb *ppe_cb) +{ + dsaf_set_dev_bit(ppe_cb, PPE_TNL_0_5_CNT_CLR_CE_REG, + PPE_CNT_CLR_CE_B, 1); +} + +/** + * hns_ppe_checksum_hw - set ppe checksum caculate + * @ppe_device: ppe device + * @value: value + */ +static void hns_ppe_checksum_hw(struct hns_ppe_cb *ppe_cb, u32 value) +{ + dsaf_set_dev_field(ppe_cb, PPE_CFG_PRO_CHECK_EN_REG, + 0xfffffff, 0, value); +} + +static void hns_ppe_set_qid_mode(struct ppe_common_cb *ppe_common, + enum ppe_qid_mode qid_mdoe) +{ + dsaf_set_dev_field(ppe_common, PPE_COM_CFG_QID_MODE_REG, + PPE_CFG_QID_MODE_CF_QID_MODE_M, + PPE_CFG_QID_MODE_CF_QID_MODE_S, qid_mdoe); +} + +/** + * hns_ppe_set_qid - set ppe qid + * @ppe_common: ppe common device + * @qid: queue id + */ +static void hns_ppe_set_qid(struct ppe_common_cb *ppe_common, u32 qid) +{ + u32 qid_mod = dsaf_read_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG); + + if (!dsaf_get_field(qid_mod, PPE_CFG_QID_MODE_DEF_QID_M, + PPE_CFG_QID_MODE_DEF_QID_S)) { + dsaf_set_field(qid_mod, PPE_CFG_QID_MODE_DEF_QID_M, + PPE_CFG_QID_MODE_DEF_QID_S, qid); + dsaf_write_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG, qid_mod); + } +} + +/** + * hns_ppe_set_port_mode - set port mode + * @ppe_device: ppe device + * @mode: port mode + */ +static void hns_ppe_set_port_mode(struct hns_ppe_cb *ppe_cb, + enum ppe_port_mode mode) +{ + dsaf_write_dev(ppe_cb, PPE_CFG_XGE_MODE_REG, mode); +} + +/** + * hns_ppe_common_init_hw - init ppe common device + * @ppe_common: ppe common device + * + * Return 0 on success, negative on failure + */ +static int hns_ppe_common_init_hw(struct ppe_common_cb *ppe_common) +{ + enum ppe_qid_mode qid_mode; + enum dsaf_mode dsaf_mode = ppe_common->dsaf_dev->dsaf_mode; + + hns_ppe_com_srst(ppe_common, 0); + mdelay(100); + hns_ppe_com_srst(ppe_common, 1); + mdelay(100); + + if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE) { + switch (dsaf_mode) { + case DSAF_MODE_ENABLE_FIX: + case DSAF_MODE_DISABLE_FIX: + qid_mode = PPE_QID_MODE0; + hns_ppe_set_qid(ppe_common, 0); + break; + case DSAF_MODE_ENABLE_0VM: + case DSAF_MODE_DISABLE_2PORT_64VM: + qid_mode = PPE_QID_MODE3; + break; + case DSAF_MODE_ENABLE_8VM: + case DSAF_MODE_DISABLE_2PORT_16VM: + qid_mode = PPE_QID_MODE4; + break; + case DSAF_MODE_ENABLE_16VM: + case DSAF_MODE_DISABLE_6PORT_0VM: + qid_mode = PPE_QID_MODE5; + break; + case DSAF_MODE_ENABLE_32VM: + case DSAF_MODE_DISABLE_6PORT_16VM: + qid_mode = PPE_QID_MODE2; + break; + case DSAF_MODE_ENABLE_128VM: + case DSAF_MODE_DISABLE_6PORT_4VM: + qid_mode = PPE_QID_MODE1; + break; + case DSAF_MODE_DISABLE_2PORT_8VM: + qid_mode = PPE_QID_MODE7; + break; + case DSAF_MODE_DISABLE_6PORT_2VM: + qid_mode = PPE_QID_MODE6; + break; + default: + dev_err(ppe_common->dev, + "get ppe queue mode failed! dsaf_mode=%d\n", + dsaf_mode); + return -EINVAL; + } + hns_ppe_set_qid_mode(ppe_common, qid_mode); + } + + dsaf_set_dev_bit(ppe_common, PPE_COM_COMMON_CNT_CLR_CE_REG, + PPE_COMMON_CNT_CLR_CE_B, 1); + + return 0; +} + +/*clr ppe exception irq*/ +static void hns_ppe_exc_irq_en(struct hns_ppe_cb *ppe_cb, int en) +{ + u32 clr_vlue = 0xfffffffful; + u32 msk_vlue = en ? 0xfffffffful : 0; /*1 is en, 0 is dis*/ + u32 vld_msk = 0; + + /*only care bit 0,1,7*/ + dsaf_set_bit(vld_msk, 0, 1); + dsaf_set_bit(vld_msk, 1, 1); + dsaf_set_bit(vld_msk, 7, 1); + + /*clr sts**/ + dsaf_write_dev(ppe_cb, PPE_RINT_REG, clr_vlue); + + /*for some reserved bits, so set 0**/ + dsaf_write_dev(ppe_cb, PPE_INTEN_REG, msk_vlue & vld_msk); +} + +/** + * ppe_init_hw - init ppe + * @ppe_device: ppe device + */ +static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb) +{ + struct ppe_common_cb *ppe_common_cb = ppe_cb->ppe_common_cb; + u32 port = ppe_cb->port; + struct dsaf_device *dsaf_dev = ppe_common_cb->dsaf_dev; + + hns_ppe_srst_by_port(dsaf_dev, port, 0); + mdelay(10); + hns_ppe_srst_by_port(dsaf_dev, port, 1); + + /* clr and msk except irq*/ + hns_ppe_exc_irq_en(ppe_cb, 0); + + if (ppe_common_cb->ppe_mode == PPE_COMMON_MODE_DEBUG) + hns_ppe_set_port_mode(ppe_cb, PPE_MODE_GE); + else + hns_ppe_set_port_mode(ppe_cb, PPE_MODE_XGE); + hns_ppe_checksum_hw(ppe_cb, 0xffffffff); + hns_ppe_cnt_clr_ce(ppe_cb); +} + +/** + * ppe_uninit_hw - uninit ppe + * @ppe_device: ppe device + */ +static void hns_ppe_uninit_hw(struct hns_ppe_cb *ppe_cb) +{ + u32 port; + + if (ppe_cb->ppe_common_cb) { + port = ppe_cb->index; + hns_ppe_srst_by_port(ppe_cb->ppe_common_cb->dsaf_dev, port, 0); + } +} + +void hns_ppe_uninit_ex(struct ppe_common_cb *ppe_common) +{ + u32 i; + + for (i = 0; i < ppe_common->ppe_num; i++) { + hns_ppe_uninit_hw(&ppe_common->ppe_cb[i]); + memset(&ppe_common->ppe_cb[i], 0, sizeof(struct hns_ppe_cb)); + } +} + +void hns_ppe_uninit(struct dsaf_device *dsaf_dev) +{ + u32 i; + + for (i = 0; i < HNS_PPE_COM_NUM; i++) { + if (dsaf_dev->ppe_common[i]) + hns_ppe_uninit_ex(dsaf_dev->ppe_common[i]); + hns_rcb_common_free_cfg(dsaf_dev, i); + hns_ppe_common_free_cfg(dsaf_dev, i); + } +} + +/** + * hns_ppe_reset - reinit ppe/rcb hw + * @dsaf_dev: dasf device + * retuen void + */ +void hns_ppe_reset_common(struct dsaf_device *dsaf_dev, u8 ppe_common_index) +{ + u32 i; + int ret; + struct ppe_common_cb *ppe_common; + + ppe_common = dsaf_dev->ppe_common[ppe_common_index]; + ret = hns_ppe_common_init_hw(ppe_common); + if (ret) + return; + + ret = hns_rcb_common_init_hw(dsaf_dev->rcb_common[ppe_common_index]); + if (ret) + return; + + for (i = 0; i < ppe_common->ppe_num; i++) + hns_ppe_init_hw(&ppe_common->ppe_cb[i]); + + hns_rcb_common_init_commit_hw(dsaf_dev->rcb_common[ppe_common_index]); +} + +void hns_ppe_update_stats(struct hns_ppe_cb *ppe_cb) +{ + struct hns_ppe_hw_stats *hw_stats = &ppe_cb->hw_stats; + + hw_stats->rx_pkts_from_sw + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_SW_PKT_CNT_REG); + hw_stats->rx_pkts + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG); + hw_stats->rx_drop_no_bd + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_NO_BUF_CNT_REG); + hw_stats->rx_alloc_buf_fail + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_FAIL_CNT_REG); + hw_stats->rx_alloc_buf_wait + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_WAIT_CNT_REG); + hw_stats->rx_drop_no_buf + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_FUL_CNT_REG); + hw_stats->rx_err_fifo_full + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_PRT_CNT_REG); + + hw_stats->tx_bd_form_rcb + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_BD_CNT_REG); + hw_stats->tx_pkts_from_rcb + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CNT_REG); + hw_stats->tx_pkts + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_OK_CNT_REG); + hw_stats->tx_err_fifo_empty + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_EPT_CNT_REG); + hw_stats->tx_err_checksum + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CS_FAIL_CNT_REG); +} + +int hns_ppe_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return ETH_PPE_STATIC_NUM; + return 0; +} + +int hns_ppe_get_regs_count(void) +{ + return ETH_PPE_DUMP_NUM; +} + +/** + * ppe_get_strings - get ppe srting + * @ppe_device: ppe device + * @stringset: string set type + * @data: output string + */ +void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data) +{ + char *buff = (char *)data; + int index = ppe_cb->index; + + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_sw_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_ok", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_drop_pkt_no_bd", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_alloc_buf_fail", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_alloc_buf_wait", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_drop_no_buf", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_err_fifo_full", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_bd", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_ok", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_err_fifo_empty", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_err_csum_fail", index); +} + +void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data) +{ + u64 *regs_buff = data; + struct hns_ppe_hw_stats *hw_stats = &ppe_cb->hw_stats; + + regs_buff[0] = hw_stats->rx_pkts_from_sw; + regs_buff[1] = hw_stats->rx_pkts; + regs_buff[2] = hw_stats->rx_drop_no_bd; + regs_buff[3] = hw_stats->rx_alloc_buf_fail; + regs_buff[4] = hw_stats->rx_alloc_buf_wait; + regs_buff[5] = hw_stats->rx_drop_no_buf; + regs_buff[6] = hw_stats->rx_err_fifo_full; + + regs_buff[7] = hw_stats->tx_bd_form_rcb; + regs_buff[8] = hw_stats->tx_pkts_from_rcb; + regs_buff[9] = hw_stats->tx_pkts; + regs_buff[10] = hw_stats->tx_err_fifo_empty; + regs_buff[11] = hw_stats->tx_err_checksum; +} + +/** + * hns_ppe_init - init ppe device + * @dsaf_dev: dasf device + * retuen 0 - success , negative --fail + */ +int hns_ppe_init(struct dsaf_device *dsaf_dev) +{ + int i, k; + int ret; + + for (i = 0; i < HNS_PPE_COM_NUM; i++) { + ret = hns_ppe_common_get_cfg(dsaf_dev, i); + if (ret) + goto get_ppe_cfg_fail; + + ret = hns_rcb_common_get_cfg(dsaf_dev, i); + if (ret) + goto get_rcb_cfg_fail; + + hns_ppe_get_cfg(dsaf_dev->ppe_common[i]); + + hns_rcb_get_cfg(dsaf_dev->rcb_common[i]); + } + + for (i = 0; i < HNS_PPE_COM_NUM; i++) + hns_ppe_reset_common(dsaf_dev, i); + + return 0; + +get_rcb_cfg_fail: + hns_ppe_common_free_cfg(dsaf_dev, i); +get_ppe_cfg_fail: + for (k = i - 1; k >= 0; k--) { + hns_rcb_common_free_cfg(dsaf_dev, k); + hns_ppe_common_free_cfg(dsaf_dev, k); + } + return ret; +} + +void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data) +{ + struct ppe_common_cb *ppe_common = ppe_cb->ppe_common_cb; + u32 *regs = data; + u32 i; + u32 offset; + + /* ppe common registers */ + regs[0] = dsaf_read_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG); + regs[1] = dsaf_read_dev(ppe_common, PPE_COM_INTEN_REG); + regs[2] = dsaf_read_dev(ppe_common, PPE_COM_RINT_REG); + regs[3] = dsaf_read_dev(ppe_common, PPE_COM_INTSTS_REG); + regs[4] = dsaf_read_dev(ppe_common, PPE_COM_COMMON_CNT_CLR_CE_REG); + + for (i = 0; i < DSAF_TOTAL_QUEUE_NUM; i++) { + offset = PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG + 0x4 * i; + regs[5 + i] = dsaf_read_dev(ppe_common, offset); + offset = PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG + 0x4 * i; + regs[5 + i + DSAF_TOTAL_QUEUE_NUM] + = dsaf_read_dev(ppe_common, offset); + offset = PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG + 0x4 * i; + regs[5 + i + DSAF_TOTAL_QUEUE_NUM * 2] + = dsaf_read_dev(ppe_common, offset); + offset = PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG + 0x4 * i; + regs[5 + i + DSAF_TOTAL_QUEUE_NUM * 3] + = dsaf_read_dev(ppe_common, offset); + } + + /* mark end of ppe regs */ + for (i = 521; i < 524; i++) + regs[i] = 0xeeeeeeee; + + /* ppe channel registers */ + regs[525] = dsaf_read_dev(ppe_cb, PPE_CFG_TX_FIFO_THRSLD_REG); + regs[526] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_THRSLD_REG); + regs[527] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_PAUSE_THRSLD_REG); + regs[528] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_SW_BP_THRSLD_REG); + regs[529] = dsaf_read_dev(ppe_cb, PPE_CFG_PAUSE_IDLE_CNT_REG); + regs[530] = dsaf_read_dev(ppe_cb, PPE_CFG_BUS_CTRL_REG); + regs[531] = dsaf_read_dev(ppe_cb, PPE_CFG_TNL_TO_BE_RST_REG); + regs[532] = dsaf_read_dev(ppe_cb, PPE_CURR_TNL_CAN_RST_REG); + + regs[533] = dsaf_read_dev(ppe_cb, PPE_CFG_XGE_MODE_REG); + regs[534] = dsaf_read_dev(ppe_cb, PPE_CFG_MAX_FRAME_LEN_REG); + regs[535] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_PKT_MODE_REG); + regs[536] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_VLAN_TAG_REG); + regs[537] = dsaf_read_dev(ppe_cb, PPE_CFG_TAG_GEN_REG); + regs[538] = dsaf_read_dev(ppe_cb, PPE_CFG_PARSE_TAG_REG); + regs[539] = dsaf_read_dev(ppe_cb, PPE_CFG_PRO_CHECK_EN_REG); + + regs[540] = dsaf_read_dev(ppe_cb, PPE_INTEN_REG); + regs[541] = dsaf_read_dev(ppe_cb, PPE_RINT_REG); + regs[542] = dsaf_read_dev(ppe_cb, PPE_INTSTS_REG); + regs[543] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_PKT_INT_REG); + + regs[544] = dsaf_read_dev(ppe_cb, PPE_CFG_HEAT_DECT_TIME0_REG); + regs[545] = dsaf_read_dev(ppe_cb, PPE_CFG_HEAT_DECT_TIME1_REG); + + /* ppe static */ + regs[546] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_SW_PKT_CNT_REG); + regs[547] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG); + regs[548] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_NO_BUF_CNT_REG); + regs[549] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_BD_CNT_REG); + regs[550] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CNT_REG); + regs[551] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_OK_CNT_REG); + regs[552] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_EPT_CNT_REG); + regs[553] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CS_FAIL_CNT_REG); + regs[554] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_FAIL_CNT_REG); + regs[555] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_WAIT_CNT_REG); + regs[556] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_FUL_CNT_REG); + regs[557] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_PRT_CNT_REG); + + regs[558] = dsaf_read_dev(ppe_cb, PPE_TNL_0_5_CNT_CLR_CE_REG); + regs[559] = dsaf_read_dev(ppe_cb, PPE_CFG_AXI_DBG_REG); + regs[560] = dsaf_read_dev(ppe_cb, PPE_HIS_PRO_ERR_REG); + regs[561] = dsaf_read_dev(ppe_cb, PPE_HIS_TNL_FIFO_ERR_REG); + regs[562] = dsaf_read_dev(ppe_cb, PPE_CURR_CFF_DATA_NUM_REG); + regs[563] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_ST_REG); + regs[564] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_ST_REG); + regs[565] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_FIFO0_REG); + regs[566] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_FIFO1_REG); + regs[567] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_FIFO0_REG); + regs[568] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_FIFO1_REG); + regs[569] = dsaf_read_dev(ppe_cb, PPE_ECO0_REG); + regs[570] = dsaf_read_dev(ppe_cb, PPE_ECO1_REG); + regs[571] = dsaf_read_dev(ppe_cb, PPE_ECO2_REG); + + /* mark end of ppe regs */ + for (i = 572; i < 576; i++) + regs[i] = 0xeeeeeeee; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h new file mode 100644 index 000000000000..4894f9a0d39f --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_DSAF_PPE_H +#define _HNS_DSAF_PPE_H + +#include <linux/platform_device.h> + +#include "hns_dsaf_main.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_rcb.h" + +#define HNS_PPE_SERVICE_NW_ENGINE_NUM DSAF_COMM_CHN +#define HNS_PPE_DEBUG_NW_ENGINE_NUM 1 +#define HNS_PPE_COM_NUM DSAF_COMM_DEV_NUM + +#define PPE_COMMON_REG_OFFSET 0x70000 +#define PPE_REG_OFFSET 0x10000 + +#define ETH_PPE_DUMP_NUM 576 +#define ETH_PPE_STATIC_NUM 12 +enum ppe_qid_mode { + PPE_QID_MODE0 = 0, /* fixed queue id mode */ + PPE_QID_MODE1, /* switch:128VM non switch:6Port/4VM/4TC */ + PPE_QID_MODE2, /* switch:32VM/4TC non switch:6Port/16VM */ + PPE_QID_MODE3, /* switch:4TC/8TAG non switch:2Port/64VM */ + PPE_QID_MODE4, /* switch:8VM/16TAG non switch:2Port/16VM/4TC */ + PPE_QID_MODE5, /* non switch:6Port/16TAG */ + PPE_QID_MODE6, /* non switch:6Port/2VM/8TC */ + PPE_QID_MODE7, /* non switch:2Port/8VM/8TC */ +}; + +enum ppe_port_mode { + PPE_MODE_GE = 0, + PPE_MODE_XGE, +}; + +enum ppe_common_mode { + PPE_COMMON_MODE_DEBUG = 0, + PPE_COMMON_MODE_SERVICE, + PPE_COMMON_MODE_MAX +}; + +struct hns_ppe_hw_stats { + u64 rx_pkts_from_sw; + u64 rx_pkts; + u64 rx_drop_no_bd; + u64 rx_alloc_buf_fail; + u64 rx_alloc_buf_wait; + u64 rx_drop_no_buf; + u64 rx_err_fifo_full; + u64 tx_bd_form_rcb; + u64 tx_pkts_from_rcb; + u64 tx_pkts; + u64 tx_err_fifo_empty; + u64 tx_err_checksum; +}; + +struct hns_ppe_cb { + struct device *dev; + struct hns_ppe_cb *next; /* pointer to next ppe device */ + struct ppe_common_cb *ppe_common_cb; /* belong to */ + struct hns_ppe_hw_stats hw_stats; + + u8 index; /* index in a ppe common device */ + u8 port; /* port id in dsaf */ + void __iomem *io_base; + int virq; +}; + +struct ppe_common_cb { + struct device *dev; + struct dsaf_device *dsaf_dev; + void __iomem *io_base; + + enum ppe_common_mode ppe_mode; + + u8 comm_index; /*ppe_common index*/ + + u32 ppe_num; + struct hns_ppe_cb ppe_cb[0]; + +}; + +int hns_ppe_init(struct dsaf_device *dsaf_dev); + +void hns_ppe_uninit(struct dsaf_device *dsaf_dev); + +void hns_ppe_reset_common(struct dsaf_device *dsaf_dev, u8 ppe_common_index); + +void hns_ppe_update_stats(struct hns_ppe_cb *ppe_cb); + +int hns_ppe_get_sset_count(int stringset); +int hns_ppe_get_regs_count(void); +void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data); + +void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data); +void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data); +#endif /* _HNS_DSAF_PPE_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c new file mode 100644 index 000000000000..05ea244d999c --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/cdev.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <asm/cacheflush.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> +#include <linux/spinlock.h> + +#include "hns_dsaf_main.h" +#include "hns_dsaf_ppe.h" +#include "hns_dsaf_rcb.h" + +#define RCB_COMMON_REG_OFFSET 0x80000 +#define TX_RING 0 +#define RX_RING 1 + +#define RCB_RESET_WAIT_TIMES 30 +#define RCB_RESET_TRY_TIMES 10 + +/** + *hns_rcb_wait_fbd_clean - clean fbd + *@qs: ring struct pointer array + *@qnum: num of array + *@flag: tx or rx flag + */ +void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag) +{ + int i, wait_cnt; + u32 fbd_num; + + for (wait_cnt = i = 0; i < q_num; wait_cnt++) { + usleep_range(200, 300); + fbd_num = 0; + if (flag & RCB_INT_FLAG_TX) + fbd_num += dsaf_read_dev(qs[i], + RCB_RING_TX_RING_FBDNUM_REG); + if (flag & RCB_INT_FLAG_RX) + fbd_num += dsaf_read_dev(qs[i], + RCB_RING_RX_RING_FBDNUM_REG); + if (!fbd_num) + i++; + if (wait_cnt >= 10000) + break; + } + + if (i < q_num) + dev_err(qs[i]->handle->owner_dev, + "queue(%d) wait fbd(%d) clean fail!!\n", i, fbd_num); +} + +/** + *hns_rcb_reset_ring_hw - ring reset + *@q: ring struct pointer + */ +void hns_rcb_reset_ring_hw(struct hnae_queue *q) +{ + u32 wait_cnt; + u32 try_cnt = 0; + u32 could_ret; + + u32 tx_fbd_num; + + while (try_cnt++ < RCB_RESET_TRY_TIMES) { + usleep_range(100, 200); + tx_fbd_num = dsaf_read_dev(q, RCB_RING_TX_RING_FBDNUM_REG); + if (tx_fbd_num) + continue; + + dsaf_write_dev(q, RCB_RING_PREFETCH_EN_REG, 0); + + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 1); + + msleep(20); + could_ret = dsaf_read_dev(q, RCB_RING_COULD_BE_RST); + + wait_cnt = 0; + while (!could_ret && (wait_cnt < RCB_RESET_WAIT_TIMES)) { + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 0); + + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 1); + + msleep(20); + could_ret = dsaf_read_dev(q, RCB_RING_COULD_BE_RST); + + wait_cnt++; + } + + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 0); + + if (could_ret) + break; + } + + if (try_cnt >= RCB_RESET_TRY_TIMES) + dev_err(q->dev->dev, "port%d reset ring fail\n", + hns_ae_get_vf_cb(q->handle)->port_index); +} + +/** + *hns_rcb_int_ctrl_hw - rcb irq enable control + *@q: hnae queue struct pointer + *@flag:ring flag tx or rx + *@mask:mask + */ +void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask) +{ + u32 int_mask_en = !!mask; + + if (flag & RCB_INT_FLAG_TX) { + dsaf_write_dev(q, RCB_RING_INTMSK_TXWL_REG, int_mask_en); + dsaf_write_dev(q, RCB_RING_INTMSK_TX_OVERTIME_REG, + int_mask_en); + } + + if (flag & RCB_INT_FLAG_RX) { + dsaf_write_dev(q, RCB_RING_INTMSK_RXWL_REG, int_mask_en); + dsaf_write_dev(q, RCB_RING_INTMSK_RX_OVERTIME_REG, + int_mask_en); + } +} + +void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag) +{ + u32 clr = 1; + + if (flag & RCB_INT_FLAG_TX) { + dsaf_write_dev(q, RCB_RING_INTSTS_TX_RING_REG, clr); + dsaf_write_dev(q, RCB_RING_INTSTS_TX_OVERTIME_REG, clr); + } + + if (flag & RCB_INT_FLAG_RX) { + dsaf_write_dev(q, RCB_RING_INTSTS_RX_RING_REG, clr); + dsaf_write_dev(q, RCB_RING_INTSTS_RX_OVERTIME_REG, clr); + } +} + +/** + *hns_rcb_ring_enable_hw - enable ring + *@ring: rcb ring + */ +void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val) +{ + dsaf_write_dev(q, RCB_RING_PREFETCH_EN_REG, !!val); +} + +void hns_rcb_start(struct hnae_queue *q, u32 val) +{ + hns_rcb_ring_enable_hw(q, val); +} + +/** + *hns_rcb_common_init_commit_hw - make rcb common init completed + *@rcb_common: rcb common device + */ +void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common) +{ + wmb(); /* Sync point before breakpoint */ + dsaf_write_dev(rcb_common, RCB_COM_CFG_SYS_FSH_REG, 1); + wmb(); /* Sync point after breakpoint */ +} + +/** + *hns_rcb_ring_init - init rcb ring + *@ring_pair: ring pair control block + *@ring_type: ring type, RX_RING or TX_RING + */ +static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type) +{ + struct hnae_queue *q = &ring_pair->q; + struct rcb_common_cb *rcb_common = ring_pair->rcb_common; + u32 bd_size_type = rcb_common->dsaf_dev->buf_size_type; + struct hnae_ring *ring = + (ring_type == RX_RING) ? &q->rx_ring : &q->tx_ring; + dma_addr_t dma = ring->desc_dma_addr; + + if (ring_type == RX_RING) { + dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_L_REG, + (u32)dma); + dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + dsaf_write_dev(q, RCB_RING_RX_RING_BD_LEN_REG, + bd_size_type); + dsaf_write_dev(q, RCB_RING_RX_RING_BD_NUM_REG, + ring_pair->port_id_in_dsa); + dsaf_write_dev(q, RCB_RING_RX_RING_PKTLINE_REG, + ring_pair->port_id_in_dsa); + } else { + dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_L_REG, + (u32)dma); + dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + dsaf_write_dev(q, RCB_RING_TX_RING_BD_LEN_REG, + bd_size_type); + dsaf_write_dev(q, RCB_RING_TX_RING_BD_NUM_REG, + ring_pair->port_id_in_dsa); + dsaf_write_dev(q, RCB_RING_TX_RING_PKTLINE_REG, + ring_pair->port_id_in_dsa); + } +} + +/** + *hns_rcb_init_hw - init rcb hardware + *@ring: rcb ring + */ +void hns_rcb_init_hw(struct ring_pair_cb *ring) +{ + hns_rcb_ring_init(ring, RX_RING); + hns_rcb_ring_init(ring, TX_RING); +} + +/** + *hns_rcb_set_port_desc_cnt - set rcb port description num + *@rcb_common: rcb_common device + *@port_idx:port index + *@desc_cnt:BD num + */ +static void hns_rcb_set_port_desc_cnt(struct rcb_common_cb *rcb_common, + u32 port_idx, u32 desc_cnt) +{ + if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM) + port_idx = 0; + + dsaf_write_dev(rcb_common, RCB_CFG_BD_NUM_REG + port_idx * 4, + desc_cnt); +} + +/** + *hns_rcb_set_port_coalesced_frames - set rcb port coalesced frames + *@rcb_common: rcb_common device + *@port_idx:port index + *@coalesced_frames:BD num for coalesced frames + */ +static int hns_rcb_set_port_coalesced_frames(struct rcb_common_cb *rcb_common, + u32 port_idx, + u32 coalesced_frames) +{ + if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM) + port_idx = 0; + if (coalesced_frames >= rcb_common->desc_num || + coalesced_frames > HNS_RCB_MAX_COALESCED_FRAMES) + return -EINVAL; + + dsaf_write_dev(rcb_common, RCB_CFG_PKTLINE_REG + port_idx * 4, + coalesced_frames); + return 0; +} + +/** + *hns_rcb_get_port_coalesced_frames - set rcb port coalesced frames + *@rcb_common: rcb_common device + *@port_idx:port index + * return coaleseced frames value + */ +static u32 hns_rcb_get_port_coalesced_frames(struct rcb_common_cb *rcb_common, + u32 port_idx) +{ + if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM) + port_idx = 0; + + return dsaf_read_dev(rcb_common, + RCB_CFG_PKTLINE_REG + port_idx * 4); +} + +/** + *hns_rcb_set_timeout - set rcb port coalesced time_out + *@rcb_common: rcb_common device + *@time_out:time for coalesced time_out + */ +static void hns_rcb_set_timeout(struct rcb_common_cb *rcb_common, + u32 timeout) +{ + dsaf_write_dev(rcb_common, RCB_CFG_OVERTIME_REG, timeout); +} + +static int hns_rcb_common_get_port_num(struct rcb_common_cb *rcb_common) +{ + if (rcb_common->comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + return HNS_RCB_SERVICE_NW_ENGINE_NUM; + else + return HNS_RCB_DEBUG_NW_ENGINE_NUM; +} + +/*clr rcb comm exception irq**/ +static void hns_rcb_comm_exc_irq_en( + struct rcb_common_cb *rcb_common, int en) +{ + u32 clr_vlue = 0xfffffffful; + u32 msk_vlue = en ? 0 : 0xfffffffful; + + /* clr int*/ + dsaf_write_dev(rcb_common, RCB_COM_INTSTS_ECC_ERR_REG, clr_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_RING_STS, clr_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_BD_RINT_STS, clr_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_RINT_TX_PKT_REG, clr_vlue); + dsaf_write_dev(rcb_common, RCB_COM_AXI_ERR_STS, clr_vlue); + + /*en msk*/ + dsaf_write_dev(rcb_common, RCB_COM_INTMASK_ECC_ERR_REG, msk_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_INTMASK_RING, msk_vlue); + + /*for tx bd neednot cacheline, so msk sf_txring_fbd_intmask (bit 1)**/ + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_INTMASK_BD, msk_vlue | 2); + + dsaf_write_dev(rcb_common, RCB_COM_INTMSK_TX_PKT_REG, msk_vlue); + dsaf_write_dev(rcb_common, RCB_COM_AXI_WR_ERR_INTMASK, msk_vlue); +} + +/** + *hns_rcb_common_init_hw - init rcb common hardware + *@rcb_common: rcb_common device + *retuen 0 - success , negative --fail + */ +int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common) +{ + u32 reg_val; + int i; + int port_num = hns_rcb_common_get_port_num(rcb_common); + + hns_rcb_comm_exc_irq_en(rcb_common, 0); + + reg_val = dsaf_read_dev(rcb_common, RCB_COM_CFG_INIT_FLAG_REG); + if (0x1 != (reg_val & 0x1)) { + dev_err(rcb_common->dsaf_dev->dev, + "RCB_COM_CFG_INIT_FLAG_REG reg = 0x%x\n", reg_val); + return -EBUSY; + } + + for (i = 0; i < port_num; i++) { + hns_rcb_set_port_desc_cnt(rcb_common, i, rcb_common->desc_num); + (void)hns_rcb_set_port_coalesced_frames( + rcb_common, i, rcb_common->coalesced_frames); + } + hns_rcb_set_timeout(rcb_common, rcb_common->timeout); + + dsaf_write_dev(rcb_common, RCB_COM_CFG_ENDIAN_REG, + HNS_RCB_COMMON_ENDIAN); + + return 0; +} + +int hns_rcb_buf_size2type(u32 buf_size) +{ + int bd_size_type; + + switch (buf_size) { + case 512: + bd_size_type = HNS_BD_SIZE_512_TYPE; + break; + case 1024: + bd_size_type = HNS_BD_SIZE_1024_TYPE; + break; + case 2048: + bd_size_type = HNS_BD_SIZE_2048_TYPE; + break; + case 4096: + bd_size_type = HNS_BD_SIZE_4096_TYPE; + break; + default: + bd_size_type = -EINVAL; + } + + return bd_size_type; +} + +static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type) +{ + struct hnae_ring *ring; + struct rcb_common_cb *rcb_common; + struct ring_pair_cb *ring_pair_cb; + u32 buf_size; + u16 desc_num; + int irq_idx; + + ring_pair_cb = container_of(q, struct ring_pair_cb, q); + if (ring_type == RX_RING) { + ring = &q->rx_ring; + ring->io_base = ring_pair_cb->q.io_base; + irq_idx = HNS_RCB_IRQ_IDX_RX; + } else { + ring = &q->tx_ring; + ring->io_base = (u8 __iomem *)ring_pair_cb->q.io_base + + HNS_RCB_TX_REG_OFFSET; + irq_idx = HNS_RCB_IRQ_IDX_TX; + } + + rcb_common = ring_pair_cb->rcb_common; + buf_size = rcb_common->dsaf_dev->buf_size; + desc_num = rcb_common->dsaf_dev->desc_num; + + ring->desc = NULL; + ring->desc_cb = NULL; + + ring->irq = ring_pair_cb->virq[irq_idx]; + ring->desc_dma_addr = 0; + + ring->buf_size = buf_size; + ring->desc_num = desc_num; + ring->max_desc_num_per_pkt = HNS_RCB_RING_MAX_BD_PER_PKT; + ring->max_raw_data_sz_per_desc = HNS_RCB_MAX_PKT_SIZE; + ring->max_pkt_size = HNS_RCB_MAX_PKT_SIZE; + ring->next_to_use = 0; + ring->next_to_clean = 0; +} + +static void hns_rcb_ring_pair_get_cfg(struct ring_pair_cb *ring_pair_cb) +{ + ring_pair_cb->q.handle = NULL; + + hns_rcb_ring_get_cfg(&ring_pair_cb->q, RX_RING); + hns_rcb_ring_get_cfg(&ring_pair_cb->q, TX_RING); +} + +static int hns_rcb_get_port(struct rcb_common_cb *rcb_common, int ring_idx) +{ + int comm_index = rcb_common->comm_index; + int port; + int q_num; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + q_num = (int)rcb_common->max_q_per_vf * rcb_common->max_vfn; + port = ring_idx / q_num; + } else { + port = HNS_RCB_SERVICE_NW_ENGINE_NUM + comm_index - 1; + } + + return port; +} + +static int hns_rcb_get_base_irq_idx(struct rcb_common_cb *rcb_common) +{ + int comm_index = rcb_common->comm_index; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + return HNS_SERVICE_RING_IRQ_IDX; + else + return HNS_DEBUG_RING_IRQ_IDX + (comm_index - 1) * 2; +} + +#define RCB_COMM_BASE_TO_RING_BASE(base, ringid)\ + ((base) + 0x10000 + HNS_RCB_REG_OFFSET * (ringid)) +/** + *hns_rcb_get_cfg - get rcb config + *@rcb_common: rcb common device + */ +void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common) +{ + struct ring_pair_cb *ring_pair_cb; + u32 i; + u32 ring_num = rcb_common->ring_num; + int base_irq_idx = hns_rcb_get_base_irq_idx(rcb_common); + struct device_node *np = rcb_common->dsaf_dev->dev->of_node; + + for (i = 0; i < ring_num; i++) { + ring_pair_cb = &rcb_common->ring_pair_cb[i]; + ring_pair_cb->rcb_common = rcb_common; + ring_pair_cb->dev = rcb_common->dsaf_dev->dev; + ring_pair_cb->index = i; + ring_pair_cb->q.io_base = + RCB_COMM_BASE_TO_RING_BASE(rcb_common->io_base, i); + ring_pair_cb->port_id_in_dsa = hns_rcb_get_port(rcb_common, i); + ring_pair_cb->virq[HNS_RCB_IRQ_IDX_TX] + = irq_of_parse_and_map(np, base_irq_idx + i * 2); + ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX] + = irq_of_parse_and_map(np, base_irq_idx + i * 2 + 1); + ring_pair_cb->q.phy_base = + RCB_COMM_BASE_TO_RING_BASE(rcb_common->phy_base, i); + hns_rcb_ring_pair_get_cfg(ring_pair_cb); + } +} + +/** + *hns_rcb_get_coalesced_frames - get rcb port coalesced frames + *@rcb_common: rcb_common device + *@comm_index:port index + *return coalesced_frames + */ +u32 hns_rcb_get_coalesced_frames(struct dsaf_device *dsaf_dev, int port) +{ + int comm_index = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + + return hns_rcb_get_port_coalesced_frames(rcb_comm, port); +} + +/** + *hns_rcb_get_coalesce_usecs - get rcb port coalesced time_out + *@rcb_common: rcb_common device + *@comm_index:port index + *return time_out + */ +u32 hns_rcb_get_coalesce_usecs(struct dsaf_device *dsaf_dev, int comm_index) +{ + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + + return rcb_comm->timeout; +} + +/** + *hns_rcb_set_coalesce_usecs - set rcb port coalesced time_out + *@rcb_common: rcb_common device + *@comm_index: comm :index + *@etx_usecs:tx time for coalesced time_out + *@rx_usecs:rx time for coalesced time_out + */ +void hns_rcb_set_coalesce_usecs(struct dsaf_device *dsaf_dev, + int port, u32 timeout) +{ + int comm_index = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + + if (rcb_comm->timeout == timeout) + return; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + dev_err(dsaf_dev->dev, + "error: not support coalesce_usecs setting!\n"); + return; + } + rcb_comm->timeout = timeout; + hns_rcb_set_timeout(rcb_comm, rcb_comm->timeout); +} + +/** + *hns_rcb_set_coalesced_frames - set rcb coalesced frames + *@rcb_common: rcb_common device + *@tx_frames:tx BD num for coalesced frames + *@rx_frames:rx BD num for coalesced frames + *Return 0 on success, negative on failure + */ +int hns_rcb_set_coalesced_frames(struct dsaf_device *dsaf_dev, + int port, u32 coalesced_frames) +{ + int comm_index = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + u32 coalesced_reg_val; + int ret; + + coalesced_reg_val = hns_rcb_get_port_coalesced_frames(rcb_comm, port); + + if (coalesced_reg_val == coalesced_frames) + return 0; + + if (coalesced_frames >= HNS_RCB_MIN_COALESCED_FRAMES) { + ret = hns_rcb_set_port_coalesced_frames(rcb_comm, port, + coalesced_frames); + return ret; + } else { + return -EINVAL; + } +} + +/** + *hns_rcb_get_queue_mode - get max VM number and max ring number per VM + * accordding to dsaf mode + *@dsaf_mode: dsaf mode + *@max_vfn : max vfn number + *@max_q_per_vf:max ring number per vm + */ +static void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index, + u16 *max_vfn, u16 *max_q_per_vf) +{ + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + switch (dsaf_mode) { + case DSAF_MODE_DISABLE_6PORT_0VM: + *max_vfn = 1; + *max_q_per_vf = 16; + break; + case DSAF_MODE_DISABLE_FIX: + *max_vfn = 1; + *max_q_per_vf = 1; + break; + case DSAF_MODE_DISABLE_2PORT_64VM: + *max_vfn = 64; + *max_q_per_vf = 1; + break; + case DSAF_MODE_DISABLE_6PORT_16VM: + *max_vfn = 16; + *max_q_per_vf = 1; + break; + default: + *max_vfn = 1; + *max_q_per_vf = 16; + break; + } + } else { + *max_vfn = 1; + *max_q_per_vf = 1; + } +} + +int hns_rcb_get_ring_num(struct dsaf_device *dsaf_dev, int comm_index) +{ + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + switch (dsaf_dev->dsaf_mode) { + case DSAF_MODE_ENABLE_FIX: + return 1; + + case DSAF_MODE_DISABLE_FIX: + return 6; + + case DSAF_MODE_ENABLE_0VM: + return 32; + + case DSAF_MODE_DISABLE_6PORT_0VM: + case DSAF_MODE_ENABLE_16VM: + case DSAF_MODE_DISABLE_6PORT_2VM: + case DSAF_MODE_DISABLE_6PORT_16VM: + case DSAF_MODE_DISABLE_6PORT_4VM: + case DSAF_MODE_ENABLE_8VM: + return 96; + + case DSAF_MODE_DISABLE_2PORT_16VM: + case DSAF_MODE_DISABLE_2PORT_8VM: + case DSAF_MODE_ENABLE_32VM: + case DSAF_MODE_DISABLE_2PORT_64VM: + case DSAF_MODE_ENABLE_128VM: + return 128; + + default: + dev_warn(dsaf_dev->dev, + "get ring num fail,use default!dsaf_mode=%d\n", + dsaf_dev->dsaf_mode); + return 128; + } + } else { + return 1; + } +} + +void __iomem *hns_rcb_common_get_vaddr(struct dsaf_device *dsaf_dev, + int comm_index) +{ + void __iomem *base_addr; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + base_addr = dsaf_dev->ppe_base + RCB_COMMON_REG_OFFSET; + else + base_addr = dsaf_dev->sds_base + + (comm_index - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET + + RCB_COMMON_REG_OFFSET; + + return base_addr; +} + +static phys_addr_t hns_rcb_common_get_paddr(struct dsaf_device *dsaf_dev, + int comm_index) +{ + struct device_node *np = dsaf_dev->dev->of_node; + phys_addr_t phy_addr; + const __be32 *tmp_addr; + u64 addr_offset = 0; + u64 size = 0; + int index = 0; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + index = 2; + addr_offset = RCB_COMMON_REG_OFFSET; + } else { + index = 1; + addr_offset = (comm_index - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET + + RCB_COMMON_REG_OFFSET; + } + tmp_addr = of_get_address(np, index, &size, NULL); + phy_addr = of_translate_address(np, tmp_addr); + return phy_addr + addr_offset; +} + +int hns_rcb_common_get_cfg(struct dsaf_device *dsaf_dev, + int comm_index) +{ + struct rcb_common_cb *rcb_common; + enum dsaf_mode dsaf_mode = dsaf_dev->dsaf_mode; + u16 max_vfn; + u16 max_q_per_vf; + int ring_num = hns_rcb_get_ring_num(dsaf_dev, comm_index); + + rcb_common = + devm_kzalloc(dsaf_dev->dev, sizeof(*rcb_common) + + ring_num * sizeof(struct ring_pair_cb), GFP_KERNEL); + if (!rcb_common) { + dev_err(dsaf_dev->dev, "rcb common devm_kzalloc fail!\n"); + return -ENOMEM; + } + rcb_common->comm_index = comm_index; + rcb_common->ring_num = ring_num; + rcb_common->dsaf_dev = dsaf_dev; + + rcb_common->desc_num = dsaf_dev->desc_num; + rcb_common->coalesced_frames = HNS_RCB_DEF_COALESCED_FRAMES; + rcb_common->timeout = HNS_RCB_MAX_TIME_OUT; + + hns_rcb_get_queue_mode(dsaf_mode, comm_index, &max_vfn, &max_q_per_vf); + rcb_common->max_vfn = max_vfn; + rcb_common->max_q_per_vf = max_q_per_vf; + + rcb_common->io_base = hns_rcb_common_get_vaddr(dsaf_dev, comm_index); + rcb_common->phy_base = hns_rcb_common_get_paddr(dsaf_dev, comm_index); + + dsaf_dev->rcb_common[comm_index] = rcb_common; + return 0; +} + +void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev, + u32 comm_index) +{ + dsaf_dev->rcb_common[comm_index] = NULL; +} + +void hns_rcb_update_stats(struct hnae_queue *queue) +{ + struct ring_pair_cb *ring = + container_of(queue, struct ring_pair_cb, q); + struct dsaf_device *dsaf_dev = ring->rcb_common->dsaf_dev; + struct ppe_common_cb *ppe_common + = dsaf_dev->ppe_common[ring->rcb_common->comm_index]; + struct hns_ring_hw_stats *hw_stats = &ring->hw_stats; + + hw_stats->rx_pkts += dsaf_read_dev(queue, + RCB_RING_RX_RING_PKTNUM_RECORD_REG); + dsaf_write_dev(queue, RCB_RING_RX_RING_PKTNUM_RECORD_REG, 0x1); + + hw_stats->ppe_rx_ok_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG + 4 * ring->index); + hw_stats->ppe_rx_drop_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG + 4 * ring->index); + + hw_stats->tx_pkts += dsaf_read_dev(queue, + RCB_RING_TX_RING_PKTNUM_RECORD_REG); + dsaf_write_dev(queue, RCB_RING_TX_RING_PKTNUM_RECORD_REG, 0x1); + + hw_stats->ppe_tx_ok_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG + 4 * ring->index); + hw_stats->ppe_tx_drop_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG + 4 * ring->index); +} + +/** + *hns_rcb_get_stats - get rcb statistic + *@ring: rcb ring + *@data:statistic value + */ +void hns_rcb_get_stats(struct hnae_queue *queue, u64 *data) +{ + u64 *regs_buff = data; + struct ring_pair_cb *ring = + container_of(queue, struct ring_pair_cb, q); + struct hns_ring_hw_stats *hw_stats = &ring->hw_stats; + + regs_buff[0] = hw_stats->tx_pkts; + regs_buff[1] = hw_stats->ppe_tx_ok_pkts; + regs_buff[2] = hw_stats->ppe_tx_drop_pkts; + regs_buff[3] = + dsaf_read_dev(queue, RCB_RING_TX_RING_FBDNUM_REG); + + regs_buff[4] = queue->tx_ring.stats.tx_pkts; + regs_buff[5] = queue->tx_ring.stats.tx_bytes; + regs_buff[6] = queue->tx_ring.stats.tx_err_cnt; + regs_buff[7] = queue->tx_ring.stats.io_err_cnt; + regs_buff[8] = queue->tx_ring.stats.sw_err_cnt; + regs_buff[9] = queue->tx_ring.stats.seg_pkt_cnt; + regs_buff[10] = queue->tx_ring.stats.restart_queue; + regs_buff[11] = queue->tx_ring.stats.tx_busy; + + regs_buff[12] = hw_stats->rx_pkts; + regs_buff[13] = hw_stats->ppe_rx_ok_pkts; + regs_buff[14] = hw_stats->ppe_rx_drop_pkts; + regs_buff[15] = + dsaf_read_dev(queue, RCB_RING_RX_RING_FBDNUM_REG); + + regs_buff[16] = queue->rx_ring.stats.rx_pkts; + regs_buff[17] = queue->rx_ring.stats.rx_bytes; + regs_buff[18] = queue->rx_ring.stats.rx_err_cnt; + regs_buff[19] = queue->rx_ring.stats.io_err_cnt; + regs_buff[20] = queue->rx_ring.stats.sw_err_cnt; + regs_buff[21] = queue->rx_ring.stats.seg_pkt_cnt; + regs_buff[22] = queue->rx_ring.stats.reuse_pg_cnt; + regs_buff[23] = queue->rx_ring.stats.err_pkt_len; + regs_buff[24] = queue->rx_ring.stats.non_vld_descs; + regs_buff[25] = queue->rx_ring.stats.err_bd_num; + regs_buff[26] = queue->rx_ring.stats.l2_err; + regs_buff[27] = queue->rx_ring.stats.l3l4_csum_err; +} + +/** + *hns_rcb_get_ring_sset_count - rcb string set count + *@stringset:ethtool cmd + *return rcb ring string set count + */ +int hns_rcb_get_ring_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return HNS_RING_STATIC_REG_NUM; + + return 0; +} + +/** + *hns_rcb_get_common_regs_count - rcb common regs count + *return regs count + */ +int hns_rcb_get_common_regs_count(void) +{ + return HNS_RCB_COMMON_DUMP_REG_NUM; +} + +/** + *rcb_get_sset_count - rcb ring regs count + *return regs count + */ +int hns_rcb_get_ring_regs_count(void) +{ + return HNS_RCB_RING_DUMP_REG_NUM; +} + +/** + *hns_rcb_get_strings - get rcb string set + *@stringset:string set index + *@data:strings name value + *@index:queue index + */ +void hns_rcb_get_strings(int stringset, u8 *data, int index) +{ + char *buff = (char *)data; + + if (stringset != ETH_SS_STATS) + return; + + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_rcb_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_ppe_tx_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_ppe_drop_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_fbd_num", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_bytes", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_err_cnt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_io_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_sw_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_seg_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_restart_queue", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_tx_busy", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_rcb_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_ppe_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_ppe_drop_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_fbd_num", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_bytes", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_err_cnt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_io_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_sw_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_seg_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_reuse_pg", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_len_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_non_vld_desc_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_bd_num_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_l2_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_l3l4csum_err", index); +} + +void hns_rcb_get_common_regs(struct rcb_common_cb *rcb_com, void *data) +{ + u32 *regs = data; + u32 i = 0; + + /*rcb common registers */ + regs[0] = dsaf_read_dev(rcb_com, RCB_COM_CFG_ENDIAN_REG); + regs[1] = dsaf_read_dev(rcb_com, RCB_COM_CFG_SYS_FSH_REG); + regs[2] = dsaf_read_dev(rcb_com, RCB_COM_CFG_INIT_FLAG_REG); + + regs[3] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PKT_REG); + regs[4] = dsaf_read_dev(rcb_com, RCB_COM_CFG_RINVLD_REG); + regs[5] = dsaf_read_dev(rcb_com, RCB_COM_CFG_FNA_REG); + regs[6] = dsaf_read_dev(rcb_com, RCB_COM_CFG_FA_REG); + regs[7] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PKT_TC_BP_REG); + regs[8] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PPE_TNL_CLKEN_REG); + + regs[9] = dsaf_read_dev(rcb_com, RCB_COM_INTMSK_TX_PKT_REG); + regs[10] = dsaf_read_dev(rcb_com, RCB_COM_RINT_TX_PKT_REG); + regs[11] = dsaf_read_dev(rcb_com, RCB_COM_INTMASK_ECC_ERR_REG); + regs[12] = dsaf_read_dev(rcb_com, RCB_COM_INTSTS_ECC_ERR_REG); + regs[13] = dsaf_read_dev(rcb_com, RCB_COM_EBD_SRAM_ERR_REG); + regs[14] = dsaf_read_dev(rcb_com, RCB_COM_RXRING_ERR_REG); + regs[15] = dsaf_read_dev(rcb_com, RCB_COM_TXRING_ERR_REG); + regs[16] = dsaf_read_dev(rcb_com, RCB_COM_TX_FBD_ERR_REG); + regs[17] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK_EN_REG); + regs[18] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK0_REG); + regs[19] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK1_REG); + regs[20] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK2_REG); + regs[21] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK3_REG); + regs[22] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK4_REG); + regs[23] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK5_REG); + regs[24] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR0_REG); + regs[25] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR3_REG); + regs[26] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR4_REG); + regs[27] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR5_REG); + + regs[28] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_INTMASK_RING); + regs[29] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_RING_STS); + regs[30] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_RING); + regs[31] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_INTMASK_BD); + regs[32] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_BD_RINT_STS); + regs[33] = dsaf_read_dev(rcb_com, RCB_COM_RCB_RD_BD_BUSY); + regs[34] = dsaf_read_dev(rcb_com, RCB_COM_RCB_FBD_CRT_EN); + regs[35] = dsaf_read_dev(rcb_com, RCB_COM_AXI_WR_ERR_INTMASK); + regs[36] = dsaf_read_dev(rcb_com, RCB_COM_AXI_ERR_STS); + regs[37] = dsaf_read_dev(rcb_com, RCB_COM_CHK_TX_FBD_NUM_REG); + + /* rcb common entry registers */ + for (i = 0; i < 16; i++) { /* total 16 model registers */ + regs[38 + i] + = dsaf_read_dev(rcb_com, RCB_CFG_BD_NUM_REG + 4 * i); + regs[54 + i] + = dsaf_read_dev(rcb_com, RCB_CFG_PKTLINE_REG + 4 * i); + } + + regs[70] = dsaf_read_dev(rcb_com, RCB_CFG_OVERTIME_REG); + regs[71] = dsaf_read_dev(rcb_com, RCB_CFG_PKTLINE_INT_NUM_REG); + regs[72] = dsaf_read_dev(rcb_com, RCB_CFG_OVERTIME_INT_NUM_REG); + + /* mark end of rcb common regs */ + for (i = 73; i < 80; i++) + regs[i] = 0xcccccccc; +} + +void hns_rcb_get_ring_regs(struct hnae_queue *queue, void *data) +{ + u32 *regs = data; + struct ring_pair_cb *ring_pair + = container_of(queue, struct ring_pair_cb, q); + u32 i = 0; + + /*rcb ring registers */ + regs[0] = dsaf_read_dev(queue, RCB_RING_RX_RING_BASEADDR_L_REG); + regs[1] = dsaf_read_dev(queue, RCB_RING_RX_RING_BASEADDR_H_REG); + regs[2] = dsaf_read_dev(queue, RCB_RING_RX_RING_BD_NUM_REG); + regs[3] = dsaf_read_dev(queue, RCB_RING_RX_RING_BD_LEN_REG); + regs[4] = dsaf_read_dev(queue, RCB_RING_RX_RING_PKTLINE_REG); + regs[5] = dsaf_read_dev(queue, RCB_RING_RX_RING_TAIL_REG); + regs[6] = dsaf_read_dev(queue, RCB_RING_RX_RING_HEAD_REG); + regs[7] = dsaf_read_dev(queue, RCB_RING_RX_RING_FBDNUM_REG); + regs[8] = dsaf_read_dev(queue, RCB_RING_RX_RING_PKTNUM_RECORD_REG); + + regs[9] = dsaf_read_dev(queue, RCB_RING_TX_RING_BASEADDR_L_REG); + regs[10] = dsaf_read_dev(queue, RCB_RING_TX_RING_BASEADDR_H_REG); + regs[11] = dsaf_read_dev(queue, RCB_RING_TX_RING_BD_NUM_REG); + regs[12] = dsaf_read_dev(queue, RCB_RING_TX_RING_BD_LEN_REG); + regs[13] = dsaf_read_dev(queue, RCB_RING_TX_RING_PKTLINE_REG); + regs[15] = dsaf_read_dev(queue, RCB_RING_TX_RING_TAIL_REG); + regs[16] = dsaf_read_dev(queue, RCB_RING_TX_RING_HEAD_REG); + regs[17] = dsaf_read_dev(queue, RCB_RING_TX_RING_FBDNUM_REG); + regs[18] = dsaf_read_dev(queue, RCB_RING_TX_RING_OFFSET_REG); + regs[19] = dsaf_read_dev(queue, RCB_RING_TX_RING_PKTNUM_RECORD_REG); + + regs[20] = dsaf_read_dev(queue, RCB_RING_PREFETCH_EN_REG); + regs[21] = dsaf_read_dev(queue, RCB_RING_CFG_VF_NUM_REG); + regs[22] = dsaf_read_dev(queue, RCB_RING_ASID_REG); + regs[23] = dsaf_read_dev(queue, RCB_RING_RX_VM_REG); + regs[24] = dsaf_read_dev(queue, RCB_RING_T0_BE_RST); + regs[25] = dsaf_read_dev(queue, RCB_RING_COULD_BE_RST); + regs[26] = dsaf_read_dev(queue, RCB_RING_WRR_WEIGHT_REG); + + regs[27] = dsaf_read_dev(queue, RCB_RING_INTMSK_RXWL_REG); + regs[28] = dsaf_read_dev(queue, RCB_RING_INTSTS_RX_RING_REG); + regs[29] = dsaf_read_dev(queue, RCB_RING_INTMSK_TXWL_REG); + regs[30] = dsaf_read_dev(queue, RCB_RING_INTSTS_TX_RING_REG); + regs[31] = dsaf_read_dev(queue, RCB_RING_INTMSK_RX_OVERTIME_REG); + regs[32] = dsaf_read_dev(queue, RCB_RING_INTSTS_RX_OVERTIME_REG); + regs[33] = dsaf_read_dev(queue, RCB_RING_INTMSK_TX_OVERTIME_REG); + regs[34] = dsaf_read_dev(queue, RCB_RING_INTSTS_TX_OVERTIME_REG); + + /* mark end of ring regs */ + for (i = 35; i < 40; i++) + regs[i] = 0xcccccc00 + ring_pair->index; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h new file mode 100644 index 000000000000..c7db6130a3cf --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_DSAF_RCB_H +#define _HNS_DSAF_RCB_H + +#include <linux/netdevice.h> +#include <linux/platform_device.h> + +#include "hnae.h" +#include "hns_dsaf_main.h" + +struct rcb_common_cb; + +#define HNS_RCB_IRQ_NUM_PER_QUEUE 2 +#define HNS_RCB_IRQ_IDX_TX 0 +#define HNS_RCB_IRQ_IDX_RX 1 +#define HNS_RCB_TX_REG_OFFSET 0x40 + +#define HNS_RCB_SERVICE_NW_ENGINE_NUM DSAF_COMM_CHN +#define HNS_RCB_DEBUG_NW_ENGINE_NUM 1 +#define HNS_RCB_RING_MAX_BD_PER_PKT 3 +#define HNS_RCB_MAX_PKT_SIZE MAC_MAX_MTU + +#define HNS_RCB_RING_MAX_PENDING_BD 1024 +#define HNS_RCB_RING_MIN_PENDING_BD 16 + +#define HNS_RCB_REG_OFFSET 0x10000 + +#define HNS_RCB_MAX_COALESCED_FRAMES 1023 +#define HNS_RCB_MIN_COALESCED_FRAMES 1 +#define HNS_RCB_DEF_COALESCED_FRAMES 50 +#define HNS_RCB_MAX_TIME_OUT 0x500 + +#define HNS_RCB_COMMON_ENDIAN 1 + +#define HNS_BD_SIZE_512_TYPE 0 +#define HNS_BD_SIZE_1024_TYPE 1 +#define HNS_BD_SIZE_2048_TYPE 2 +#define HNS_BD_SIZE_4096_TYPE 3 + +#define HNS_RCB_COMMON_DUMP_REG_NUM 80 +#define HNS_RCB_RING_DUMP_REG_NUM 40 +#define HNS_RING_STATIC_REG_NUM 28 + +#define HNS_DUMP_REG_NUM 500 +#define HNS_STATIC_REG_NUM 12 + +enum rcb_int_flag { + RCB_INT_FLAG_TX = 0x1, + RCB_INT_FLAG_RX = (0x1 << 1), + RCB_INT_FLAG_MAX = (0x1 << 2), /*must be the last element */ +}; + +struct hns_ring_hw_stats { + u64 tx_pkts; + u64 ppe_tx_ok_pkts; + u64 ppe_tx_drop_pkts; + u64 rx_pkts; + u64 ppe_rx_ok_pkts; + u64 ppe_rx_drop_pkts; +}; + +struct ring_pair_cb { + struct rcb_common_cb *rcb_common; /* ring belongs to */ + struct device *dev; /*device for DMA mapping */ + struct hnae_queue q; + + u16 index; /* global index in a rcb common device */ + u16 buf_size; + + int virq[HNS_RCB_IRQ_NUM_PER_QUEUE]; + + u8 port_id_in_dsa; + u8 used_by_vf; + + struct hns_ring_hw_stats hw_stats; +}; + +struct rcb_common_cb { + u8 __iomem *io_base; + phys_addr_t phy_base; + struct dsaf_device *dsaf_dev; + u16 max_vfn; + u16 max_q_per_vf; + + u8 comm_index; + u32 ring_num; + u32 coalesced_frames; /* frames threshold of rx interrupt */ + u32 timeout; /* time threshold of rx interrupt */ + u32 desc_num; /* desc num per queue*/ + + struct ring_pair_cb ring_pair_cb[0]; +}; + +int hns_rcb_buf_size2type(u32 buf_size); + +int hns_rcb_common_get_cfg(struct dsaf_device *dsaf_dev, int comm_index); +void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index); +int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common); +void hns_rcb_start(struct hnae_queue *q, u32 val); +void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common); +void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common); + +void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val); +void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag); +void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 enable); +void hns_rcb_init_hw(struct ring_pair_cb *ring); +void hns_rcb_reset_ring_hw(struct hnae_queue *q); +void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag); + +u32 hns_rcb_get_coalesced_frames(struct dsaf_device *dsaf_dev, int comm_index); +u32 hns_rcb_get_coalesce_usecs(struct dsaf_device *dsaf_dev, int comm_index); +void hns_rcb_set_coalesce_usecs(struct dsaf_device *dsaf_dev, + int comm_index, u32 timeout); +int hns_rcb_set_coalesced_frames(struct dsaf_device *dsaf_dev, + int comm_index, u32 coalesce_frames); +void hns_rcb_update_stats(struct hnae_queue *queue); + +void hns_rcb_get_stats(struct hnae_queue *queue, u64 *data); + +void hns_rcb_get_common_regs(struct rcb_common_cb *rcb_common, void *data); + +int hns_rcb_get_ring_sset_count(int stringset); +int hns_rcb_get_common_regs_count(void); +int hns_rcb_get_ring_regs_count(void); + +void hns_rcb_get_ring_regs(struct hnae_queue *queue, void *data); + +void hns_rcb_get_strings(int stringset, u8 *data, int index); +#endif /* _HNS_DSAF_RCB_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h new file mode 100644 index 000000000000..b475e1bf2e6f --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -0,0 +1,972 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _DSAF_REG_H_ +#define _DSAF_REG_H_ + +#define HNS_GE_FIFO_ERR_INTNUM 8 +#define HNS_XGE_ERR_INTNUM 6 +#define HNS_RCB_COMM_ERR_INTNUM 12 +#define HNS_PPE_TNL_ERR_INTNUM 8 +#define HNS_DSAF_EVENT_INTNUM 21 +#define HNS_DEBUG_RING_INTNUM 4 +#define HNS_SERVICE_RING_INTNUM 256 + +#define HNS_DEBUG_RING_IRQ_IDX (HNS_GE_FIFO_ERR_INTNUM + HNS_XGE_ERR_INTNUM +\ + HNS_RCB_COMM_ERR_INTNUM + HNS_PPE_TNL_ERR_INTNUM +\ + HNS_DSAF_EVENT_INTNUM) +#define HNS_SERVICE_RING_IRQ_IDX (HNS_DEBUG_RING_IRQ_IDX +\ + HNS_DEBUG_RING_INTNUM) + +#define DSAF_IRQ_NUM 18 + +#define DSAF_MAX_PORT_NUM_PER_CHIP 8 +#define DSAF_SERVICE_PORT_NUM_PER_DSAF 6 +#define DSAF_MAX_VM_NUM 128 + +#define DSAF_COMM_DEV_NUM 3 +#define DSAF_PPE_INODE_BASE 6 +#define HNS_DSAF_COMM_SERVICE_NW_IDX 0 +#define DSAF_DEBUG_NW_NUM 2 +#define DSAF_SERVICE_NW_NUM 6 +#define DSAF_COMM_CHN DSAF_SERVICE_NW_NUM +#define DSAF_GE_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM)) +#define DSAF_PORT_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM)) +#define DSAF_XGE_NUM DSAF_SERVICE_NW_NUM +#define DSAF_NODE_NUM 18 +#define DSAF_XOD_BIG_NUM DSAF_NODE_NUM +#define DSAF_SBM_NUM DSAF_NODE_NUM +#define DSAF_VOQ_NUM DSAF_NODE_NUM +#define DSAF_INODE_NUM DSAF_NODE_NUM +#define DSAF_XOD_NUM 8 +#define DSAF_TBL_NUM 8 +#define DSAF_SW_PORT_NUM 8 +#define DSAF_TOTAL_QUEUE_NUM 129 + +#define DSAF_TCAM_SUM 512 +#define DSAF_LINE_SUM (2048 * 14) + +#define DSAF_SUB_SC_NT_SRAM_CLK_SEL_REG 0x100 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG 0x180 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL1_REG 0x184 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL2_REG 0x188 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL3_REG 0x18C +#define DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG 0x190 +#define DSAF_SUB_SC_HILINK4_CRG_CTRL1_REG 0x194 +#define DSAF_SUB_SC_DSAF_CLK_EN_REG 0x300 +#define DSAF_SUB_SC_DSAF_CLK_DIS_REG 0x304 +#define DSAF_SUB_SC_NT_CLK_EN_REG 0x308 +#define DSAF_SUB_SC_NT_CLK_DIS_REG 0x30C +#define DSAF_SUB_SC_XGE_CLK_EN_REG 0x310 +#define DSAF_SUB_SC_XGE_CLK_DIS_REG 0x314 +#define DSAF_SUB_SC_GE_CLK_EN_REG 0x318 +#define DSAF_SUB_SC_GE_CLK_DIS_REG 0x31C +#define DSAF_SUB_SC_PPE_CLK_EN_REG 0x320 +#define DSAF_SUB_SC_PPE_CLK_DIS_REG 0x324 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_EN_REG 0x350 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_DIS_REG 0x354 +#define DSAF_SUB_SC_XBAR_RESET_REQ_REG 0xA00 +#define DSAF_SUB_SC_XBAR_RESET_DREQ_REG 0xA04 +#define DSAF_SUB_SC_NT_RESET_REQ_REG 0xA08 +#define DSAF_SUB_SC_NT_RESET_DREQ_REG 0xA0C +#define DSAF_SUB_SC_XGE_RESET_REQ_REG 0xA10 +#define DSAF_SUB_SC_XGE_RESET_DREQ_REG 0xA14 +#define DSAF_SUB_SC_GE_RESET_REQ0_REG 0xA18 +#define DSAF_SUB_SC_GE_RESET_DREQ0_REG 0xA1C +#define DSAF_SUB_SC_GE_RESET_REQ1_REG 0xA20 +#define DSAF_SUB_SC_GE_RESET_DREQ1_REG 0xA24 +#define DSAF_SUB_SC_PPE_RESET_REQ_REG 0xA48 +#define DSAF_SUB_SC_PPE_RESET_DREQ_REG 0xA4C +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG 0xA88 +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG 0xA8C +#define DSAF_SUB_SC_LIGHT_MODULE_DETECT_EN_REG 0x2060 +#define DSAF_SUB_SC_TCAM_MBIST_EN_REG 0x2300 +#define DSAF_SUB_SC_DSAF_CLK_ST_REG 0x5300 +#define DSAF_SUB_SC_NT_CLK_ST_REG 0x5304 +#define DSAF_SUB_SC_XGE_CLK_ST_REG 0x5308 +#define DSAF_SUB_SC_GE_CLK_ST_REG 0x530C +#define DSAF_SUB_SC_PPE_CLK_ST_REG 0x5310 +#define DSAF_SUB_SC_ROCEE_CLK_ST_REG 0x5314 +#define DSAF_SUB_SC_CPU_CLK_ST_REG 0x5318 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_ST_REG 0x5328 +#define DSAF_SUB_SC_XBAR_RESET_ST_REG 0x5A00 +#define DSAF_SUB_SC_NT_RESET_ST_REG 0x5A04 +#define DSAF_SUB_SC_XGE_RESET_ST_REG 0x5A08 +#define DSAF_SUB_SC_GE_RESET_ST0_REG 0x5A0C +#define DSAF_SUB_SC_GE_RESET_ST1_REG 0x5A10 +#define DSAF_SUB_SC_PPE_RESET_ST_REG 0x5A24 +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_ST_REG 0x5A44 + +/*serdes offset**/ +#define HNS_MAC_HILINK3_REG DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG +#define HNS_MAC_HILINK4_REG DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG +#define HNS_MAC_LANE0_CTLEDFE_REG 0x000BFFCCULL +#define HNS_MAC_LANE1_CTLEDFE_REG 0x000BFFBCULL +#define HNS_MAC_LANE2_CTLEDFE_REG 0x000BFFACULL +#define HNS_MAC_LANE3_CTLEDFE_REG 0x000BFF9CULL +#define HNS_MAC_LANE0_STATE_REG 0x000BFFD4ULL +#define HNS_MAC_LANE1_STATE_REG 0x000BFFC4ULL +#define HNS_MAC_LANE2_STATE_REG 0x000BFFB4ULL +#define HNS_MAC_LANE3_STATE_REG 0x000BFFA4ULL + +#define HILINK_RESET_TIMOUT 10000 + +#define DSAF_SRAM_INIT_OVER_0_REG 0x0 +#define DSAF_CFG_0_REG 0x4 +#define DSAF_ECC_ERR_INVERT_0_REG 0x8 +#define DSAF_ABNORMAL_TIMEOUT_0_REG 0x1C +#define DSAF_FSM_TIMEOUT_0_REG 0x20 +#define DSAF_DSA_REG_CNT_CLR_CE_REG 0x2C +#define DSAF_DSA_SBM_INF_FIFO_THRD_REG 0x30 +#define DSAF_DSA_SRAM_1BIT_ECC_SEL_REG 0x34 +#define DSAF_DSA_SRAM_1BIT_ECC_CNT_REG 0x38 +#define DSAF_PFC_EN_0_REG 0x50 +#define DSAF_PFC_UNIT_CNT_0_REG 0x70 +#define DSAF_XGE_INT_MSK_0_REG 0x100 +#define DSAF_PPE_INT_MSK_0_REG 0x120 +#define DSAF_ROCEE_INT_MSK_0_REG 0x140 +#define DSAF_XGE_INT_SRC_0_REG 0x160 +#define DSAF_PPE_INT_SRC_0_REG 0x180 +#define DSAF_ROCEE_INT_SRC_0_REG 0x1A0 +#define DSAF_XGE_INT_STS_0_REG 0x1C0 +#define DSAF_PPE_INT_STS_0_REG 0x1E0 +#define DSAF_ROCEE_INT_STS_0_REG 0x200 +#define DSAF_PPE_QID_CFG_0_REG 0x300 +#define DSAF_SW_PORT_TYPE_0_REG 0x320 +#define DSAF_STP_PORT_TYPE_0_REG 0x340 +#define DSAF_MIX_DEF_QID_0_REG 0x360 +#define DSAF_PORT_DEF_VLAN_0_REG 0x380 +#define DSAF_VM_DEF_VLAN_0_REG 0x400 + +#define DSAF_INODE_CUT_THROUGH_CFG_0_REG 0x1000 +#define DSAF_INODE_ECC_INVERT_EN_0_REG 0x1008 +#define DSAF_INODE_ECC_ERR_ADDR_0_REG 0x100C +#define DSAF_INODE_IN_PORT_NUM_0_REG 0x1018 +#define DSAF_INODE_PRI_TC_CFG_0_REG 0x101C +#define DSAF_INODE_BP_STATUS_0_REG 0x1020 +#define DSAF_INODE_PAD_DISCARD_NUM_0_REG 0x1028 +#define DSAF_INODE_FINAL_IN_MAN_NUM_0_REG 0x102C +#define DSAF_INODE_FINAL_IN_PKT_NUM_0_REG 0x1030 +#define DSAF_INODE_SBM_PID_NUM_0_REG 0x1038 +#define DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG 0x103C +#define DSAF_INODE_SBM_RELS_NUM_0_REG 0x104C +#define DSAF_INODE_SBM_DROP_NUM_0_REG 0x1050 +#define DSAF_INODE_CRC_FALSE_NUM_0_REG 0x1054 +#define DSAF_INODE_BP_DISCARD_NUM_0_REG 0x1058 +#define DSAF_INODE_RSLT_DISCARD_NUM_0_REG 0x105C +#define DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG 0x1060 +#define DSAF_INODE_VOQ_OVER_NUM_0_REG 0x1068 +#define DSAF_INODE_BD_SAVE_STATUS_0_REG 0x1900 +#define DSAF_INODE_BD_ORDER_STATUS_0_REG 0x1950 +#define DSAF_INODE_SW_VLAN_TAG_DISC_0_REG 0x1A00 +#define DSAF_INODE_IN_DATA_STP_DISC_0_REG 0x1A50 +#define DSAF_INODE_GE_FC_EN_0_REG 0x1B00 +#define DSAF_INODE_VC0_IN_PKT_NUM_0_REG 0x1B50 +#define DSAF_INODE_VC1_IN_PKT_NUM_0_REG 0x1C00 + +#define DSAF_SBM_CFG_REG_0_REG 0x2000 +#define DSAF_SBM_BP_CFG_0_XGE_REG_0_REG 0x2004 +#define DSAF_SBM_BP_CFG_0_PPE_REG_0_REG 0x2304 +#define DSAF_SBM_BP_CFG_0_ROCEE_REG_0_REG 0x2604 +#define DSAF_SBM_BP_CFG_1_REG_0_REG 0x2008 +#define DSAF_SBM_BP_CFG_2_XGE_REG_0_REG 0x200C +#define DSAF_SBM_BP_CFG_2_PPE_REG_0_REG 0x230C +#define DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG 0x260C +#define DSAF_SBM_FREE_CNT_0_0_REG 0x2010 +#define DSAF_SBM_FREE_CNT_1_0_REG 0x2014 +#define DSAF_SBM_BP_CNT_0_0_REG 0x2018 +#define DSAF_SBM_BP_CNT_1_0_REG 0x201C +#define DSAF_SBM_BP_CNT_2_0_REG 0x2020 +#define DSAF_SBM_BP_CNT_3_0_REG 0x2024 +#define DSAF_SBM_INER_ST_0_REG 0x2028 +#define DSAF_SBM_MIB_REQ_FAILED_TC_0_REG 0x202C +#define DSAF_SBM_LNK_INPORT_CNT_0_REG 0x2030 +#define DSAF_SBM_LNK_DROP_CNT_0_REG 0x2034 +#define DSAF_SBM_INF_OUTPORT_CNT_0_REG 0x2038 +#define DSAF_SBM_LNK_INPORT_TC0_CNT_0_REG 0x203C +#define DSAF_SBM_LNK_INPORT_TC1_CNT_0_REG 0x2040 +#define DSAF_SBM_LNK_INPORT_TC2_CNT_0_REG 0x2044 +#define DSAF_SBM_LNK_INPORT_TC3_CNT_0_REG 0x2048 +#define DSAF_SBM_LNK_INPORT_TC4_CNT_0_REG 0x204C +#define DSAF_SBM_LNK_INPORT_TC5_CNT_0_REG 0x2050 +#define DSAF_SBM_LNK_INPORT_TC6_CNT_0_REG 0x2054 +#define DSAF_SBM_LNK_INPORT_TC7_CNT_0_REG 0x2058 +#define DSAF_SBM_LNK_REQ_CNT_0_REG 0x205C +#define DSAF_SBM_LNK_RELS_CNT_0_REG 0x2060 +#define DSAF_SBM_BP_CFG_3_REG_0_REG 0x2068 +#define DSAF_SBM_BP_CFG_4_REG_0_REG 0x206C + +#define DSAF_XOD_ETS_TSA_TC0_TC3_CFG_0_REG 0x3000 +#define DSAF_XOD_ETS_TSA_TC4_TC7_CFG_0_REG 0x3004 +#define DSAF_XOD_ETS_BW_TC0_TC3_CFG_0_REG 0x3008 +#define DSAF_XOD_ETS_BW_TC4_TC7_CFG_0_REG 0x300C +#define DSAF_XOD_ETS_BW_OFFSET_CFG_0_REG 0x3010 +#define DSAF_XOD_ETS_TOKEN_CFG_0_REG 0x3014 +#define DSAF_XOD_PFS_CFG_0_0_REG 0x3018 +#define DSAF_XOD_PFS_CFG_1_0_REG 0x301C +#define DSAF_XOD_PFS_CFG_2_0_REG 0x3020 +#define DSAF_XOD_GNT_L_0_REG 0x3024 +#define DSAF_XOD_GNT_H_0_REG 0x3028 +#define DSAF_XOD_CONNECT_STATE_0_REG 0x302C +#define DSAF_XOD_RCVPKT_CNT_0_REG 0x3030 +#define DSAF_XOD_RCVTC0_CNT_0_REG 0x3034 +#define DSAF_XOD_RCVTC1_CNT_0_REG 0x3038 +#define DSAF_XOD_RCVTC2_CNT_0_REG 0x303C +#define DSAF_XOD_RCVTC3_CNT_0_REG 0x3040 +#define DSAF_XOD_RCVVC0_CNT_0_REG 0x3044 +#define DSAF_XOD_RCVVC1_CNT_0_REG 0x3048 +#define DSAF_XOD_XGE_RCVIN0_CNT_0_REG 0x304C +#define DSAF_XOD_XGE_RCVIN1_CNT_0_REG 0x3050 +#define DSAF_XOD_XGE_RCVIN2_CNT_0_REG 0x3054 +#define DSAF_XOD_XGE_RCVIN3_CNT_0_REG 0x3058 +#define DSAF_XOD_XGE_RCVIN4_CNT_0_REG 0x305C +#define DSAF_XOD_XGE_RCVIN5_CNT_0_REG 0x3060 +#define DSAF_XOD_XGE_RCVIN6_CNT_0_REG 0x3064 +#define DSAF_XOD_XGE_RCVIN7_CNT_0_REG 0x3068 +#define DSAF_XOD_PPE_RCVIN0_CNT_0_REG 0x306C +#define DSAF_XOD_PPE_RCVIN1_CNT_0_REG 0x3070 +#define DSAF_XOD_ROCEE_RCVIN0_CNT_0_REG 0x3074 +#define DSAF_XOD_ROCEE_RCVIN1_CNT_0_REG 0x3078 +#define DSAF_XOD_FIFO_STATUS_0_REG 0x307C + +#define DSAF_VOQ_ECC_INVERT_EN_0_REG 0x4004 +#define DSAF_VOQ_SRAM_PKT_NUM_0_REG 0x4008 +#define DSAF_VOQ_IN_PKT_NUM_0_REG 0x400C +#define DSAF_VOQ_OUT_PKT_NUM_0_REG 0x4010 +#define DSAF_VOQ_ECC_ERR_ADDR_0_REG 0x4014 +#define DSAF_VOQ_BP_STATUS_0_REG 0x4018 +#define DSAF_VOQ_SPUP_IDLE_0_REG 0x401C +#define DSAF_VOQ_XGE_XOD_REQ_0_0_REG 0x4024 +#define DSAF_VOQ_XGE_XOD_REQ_1_0_REG 0x4028 +#define DSAF_VOQ_PPE_XOD_REQ_0_REG 0x402C +#define DSAF_VOQ_ROCEE_XOD_REQ_0_REG 0x4030 +#define DSAF_VOQ_BP_ALL_THRD_0_REG 0x4034 + +#define DSAF_TBL_CTRL_0_REG 0x5000 +#define DSAF_TBL_INT_MSK_0_REG 0x5004 +#define DSAF_TBL_INT_SRC_0_REG 0x5008 +#define DSAF_TBL_INT_STS_0_REG 0x5100 +#define DSAF_TBL_TCAM_ADDR_0_REG 0x500C +#define DSAF_TBL_LINE_ADDR_0_REG 0x5010 +#define DSAF_TBL_TCAM_HIGH_0_REG 0x5014 +#define DSAF_TBL_TCAM_LOW_0_REG 0x5018 +#define DSAF_TBL_TCAM_MCAST_CFG_4_0_REG 0x501C +#define DSAF_TBL_TCAM_MCAST_CFG_3_0_REG 0x5020 +#define DSAF_TBL_TCAM_MCAST_CFG_2_0_REG 0x5024 +#define DSAF_TBL_TCAM_MCAST_CFG_1_0_REG 0x5028 +#define DSAF_TBL_TCAM_MCAST_CFG_0_0_REG 0x502C +#define DSAF_TBL_TCAM_UCAST_CFG_0_REG 0x5030 +#define DSAF_TBL_LIN_CFG_0_REG 0x5034 +#define DSAF_TBL_TCAM_RDATA_HIGH_0_REG 0x5038 +#define DSAF_TBL_TCAM_RDATA_LOW_0_REG 0x503C +#define DSAF_TBL_TCAM_RAM_RDATA4_0_REG 0x5040 +#define DSAF_TBL_TCAM_RAM_RDATA3_0_REG 0x5044 +#define DSAF_TBL_TCAM_RAM_RDATA2_0_REG 0x5048 +#define DSAF_TBL_TCAM_RAM_RDATA1_0_REG 0x504C +#define DSAF_TBL_TCAM_RAM_RDATA0_0_REG 0x5050 +#define DSAF_TBL_LIN_RDATA_0_REG 0x5054 +#define DSAF_TBL_DA0_MIS_INFO1_0_REG 0x5058 +#define DSAF_TBL_DA0_MIS_INFO0_0_REG 0x505C +#define DSAF_TBL_SA_MIS_INFO2_0_REG 0x5104 +#define DSAF_TBL_SA_MIS_INFO1_0_REG 0x5098 +#define DSAF_TBL_SA_MIS_INFO0_0_REG 0x509C +#define DSAF_TBL_PUL_0_REG 0x50A0 +#define DSAF_TBL_OLD_RSLT_0_REG 0x50A4 +#define DSAF_TBL_OLD_SCAN_VAL_0_REG 0x50A8 +#define DSAF_TBL_DFX_CTRL_0_REG 0x50AC +#define DSAF_TBL_DFX_STAT_0_REG 0x50B0 +#define DSAF_TBL_DFX_STAT_2_0_REG 0x5108 +#define DSAF_TBL_LKUP_NUM_I_0_REG 0x50C0 +#define DSAF_TBL_LKUP_NUM_O_0_REG 0x50E0 +#define DSAF_TBL_UCAST_BCAST_MIS_INFO_0_0_REG 0x510C + +#define DSAF_INODE_FIFO_WL_0_REG 0x6000 +#define DSAF_ONODE_FIFO_WL_0_REG 0x6020 +#define DSAF_XGE_GE_WORK_MODE_0_REG 0x6040 +#define DSAF_XGE_APP_RX_LINK_UP_0_REG 0x6080 +#define DSAF_NETPORT_CTRL_SIG_0_REG 0x60A0 +#define DSAF_XGE_CTRL_SIG_CFG_0_REG 0x60C0 + +#define PPE_COM_CFG_QID_MODE_REG 0x0 +#define PPE_COM_INTEN_REG 0x110 +#define PPE_COM_RINT_REG 0x114 +#define PPE_COM_INTSTS_REG 0x118 +#define PPE_COM_COMMON_CNT_CLR_CE_REG 0x1120 +#define PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG 0x300 +#define PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG 0x600 +#define PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG 0x900 +#define PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG 0xC00 +#define PPE_COM_COMMON_CNT_CLR_CE_REG 0x1120 + +#define PPE_CFG_TX_FIFO_THRSLD_REG 0x0 +#define PPE_CFG_RX_FIFO_THRSLD_REG 0x4 +#define PPE_CFG_RX_FIFO_PAUSE_THRSLD_REG 0x8 +#define PPE_CFG_RX_FIFO_SW_BP_THRSLD_REG 0xC +#define PPE_CFG_PAUSE_IDLE_CNT_REG 0x10 +#define PPE_CFG_BUS_CTRL_REG 0x40 +#define PPE_CFG_TNL_TO_BE_RST_REG 0x48 +#define PPE_CURR_TNL_CAN_RST_REG 0x4C +#define PPE_CFG_XGE_MODE_REG 0x80 +#define PPE_CFG_MAX_FRAME_LEN_REG 0x84 +#define PPE_CFG_RX_PKT_MODE_REG 0x88 +#define PPE_CFG_RX_VLAN_TAG_REG 0x8C +#define PPE_CFG_TAG_GEN_REG 0x90 +#define PPE_CFG_PARSE_TAG_REG 0x94 +#define PPE_CFG_PRO_CHECK_EN_REG 0x98 +#define PPE_INTEN_REG 0x100 +#define PPE_RINT_REG 0x104 +#define PPE_INTSTS_REG 0x108 +#define PPE_CFG_RX_PKT_INT_REG 0x140 +#define PPE_CFG_HEAT_DECT_TIME0_REG 0x144 +#define PPE_CFG_HEAT_DECT_TIME1_REG 0x148 +#define PPE_HIS_RX_SW_PKT_CNT_REG 0x200 +#define PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG 0x204 +#define PPE_HIS_RX_PKT_NO_BUF_CNT_REG 0x208 +#define PPE_HIS_TX_BD_CNT_REG 0x20C +#define PPE_HIS_TX_PKT_CNT_REG 0x210 +#define PPE_HIS_TX_PKT_OK_CNT_REG 0x214 +#define PPE_HIS_TX_PKT_EPT_CNT_REG 0x218 +#define PPE_HIS_TX_PKT_CS_FAIL_CNT_REG 0x21C +#define PPE_HIS_RX_APP_BUF_FAIL_CNT_REG 0x220 +#define PPE_HIS_RX_APP_BUF_WAIT_CNT_REG 0x224 +#define PPE_HIS_RX_PKT_DROP_FUL_CNT_REG 0x228 +#define PPE_HIS_RX_PKT_DROP_PRT_CNT_REG 0x22C +#define PPE_TNL_0_5_CNT_CLR_CE_REG 0x300 +#define PPE_CFG_AXI_DBG_REG 0x304 +#define PPE_HIS_PRO_ERR_REG 0x308 +#define PPE_HIS_TNL_FIFO_ERR_REG 0x30C +#define PPE_CURR_CFF_DATA_NUM_REG 0x310 +#define PPE_CURR_RX_ST_REG 0x314 +#define PPE_CURR_TX_ST_REG 0x318 +#define PPE_CURR_RX_FIFO0_REG 0x31C +#define PPE_CURR_RX_FIFO1_REG 0x320 +#define PPE_CURR_TX_FIFO0_REG 0x324 +#define PPE_CURR_TX_FIFO1_REG 0x328 +#define PPE_ECO0_REG 0x32C +#define PPE_ECO1_REG 0x330 +#define PPE_ECO2_REG 0x334 + +#define RCB_COM_CFG_ENDIAN_REG 0x0 +#define RCB_COM_CFG_SYS_FSH_REG 0xC +#define RCB_COM_CFG_INIT_FLAG_REG 0x10 +#define RCB_COM_CFG_PKT_REG 0x30 +#define RCB_COM_CFG_RINVLD_REG 0x34 +#define RCB_COM_CFG_FNA_REG 0x38 +#define RCB_COM_CFG_FA_REG 0x3C +#define RCB_COM_CFG_PKT_TC_BP_REG 0x40 +#define RCB_COM_CFG_PPE_TNL_CLKEN_REG 0x44 + +#define RCB_COM_INTMSK_TX_PKT_REG 0x3A0 +#define RCB_COM_RINT_TX_PKT_REG 0x3A8 +#define RCB_COM_INTMASK_ECC_ERR_REG 0x400 +#define RCB_COM_INTSTS_ECC_ERR_REG 0x408 +#define RCB_COM_EBD_SRAM_ERR_REG 0x410 +#define RCB_COM_RXRING_ERR_REG 0x41C +#define RCB_COM_TXRING_ERR_REG 0x420 +#define RCB_COM_TX_FBD_ERR_REG 0x424 +#define RCB_SRAM_ECC_CHK_EN_REG 0x428 +#define RCB_SRAM_ECC_CHK0_REG 0x42C +#define RCB_SRAM_ECC_CHK1_REG 0x430 +#define RCB_SRAM_ECC_CHK2_REG 0x434 +#define RCB_SRAM_ECC_CHK3_REG 0x438 +#define RCB_SRAM_ECC_CHK4_REG 0x43c +#define RCB_SRAM_ECC_CHK5_REG 0x440 +#define RCB_ECC_ERR_ADDR0_REG 0x450 +#define RCB_ECC_ERR_ADDR3_REG 0x45C +#define RCB_ECC_ERR_ADDR4_REG 0x460 +#define RCB_ECC_ERR_ADDR5_REG 0x464 + +#define RCB_COM_SF_CFG_INTMASK_RING 0x480 +#define RCB_COM_SF_CFG_RING_STS 0x484 +#define RCB_COM_SF_CFG_RING 0x488 +#define RCB_COM_SF_CFG_INTMASK_BD 0x48C +#define RCB_COM_SF_CFG_BD_RINT_STS 0x470 +#define RCB_COM_RCB_RD_BD_BUSY 0x490 +#define RCB_COM_RCB_FBD_CRT_EN 0x494 +#define RCB_COM_AXI_WR_ERR_INTMASK 0x498 +#define RCB_COM_AXI_ERR_STS 0x49C +#define RCB_COM_CHK_TX_FBD_NUM_REG 0x4a0 + +#define RCB_CFG_BD_NUM_REG 0x9000 +#define RCB_CFG_PKTLINE_REG 0x9050 + +#define RCB_CFG_OVERTIME_REG 0x9300 +#define RCB_CFG_PKTLINE_INT_NUM_REG 0x9304 +#define RCB_CFG_OVERTIME_INT_NUM_REG 0x9308 + +#define RCB_RING_RX_RING_BASEADDR_L_REG 0x00000 +#define RCB_RING_RX_RING_BASEADDR_H_REG 0x00004 +#define RCB_RING_RX_RING_BD_NUM_REG 0x00008 +#define RCB_RING_RX_RING_BD_LEN_REG 0x0000C +#define RCB_RING_RX_RING_PKTLINE_REG 0x00010 +#define RCB_RING_RX_RING_TAIL_REG 0x00018 +#define RCB_RING_RX_RING_HEAD_REG 0x0001C +#define RCB_RING_RX_RING_FBDNUM_REG 0x00020 +#define RCB_RING_RX_RING_PKTNUM_RECORD_REG 0x0002C + +#define RCB_RING_TX_RING_BASEADDR_L_REG 0x00040 +#define RCB_RING_TX_RING_BASEADDR_H_REG 0x00044 +#define RCB_RING_TX_RING_BD_NUM_REG 0x00048 +#define RCB_RING_TX_RING_BD_LEN_REG 0x0004C +#define RCB_RING_TX_RING_PKTLINE_REG 0x00050 +#define RCB_RING_TX_RING_TAIL_REG 0x00058 +#define RCB_RING_TX_RING_HEAD_REG 0x0005C +#define RCB_RING_TX_RING_FBDNUM_REG 0x00060 +#define RCB_RING_TX_RING_OFFSET_REG 0x00064 +#define RCB_RING_TX_RING_PKTNUM_RECORD_REG 0x0006C + +#define RCB_RING_PREFETCH_EN_REG 0x0007C +#define RCB_RING_CFG_VF_NUM_REG 0x00080 +#define RCB_RING_ASID_REG 0x0008C +#define RCB_RING_RX_VM_REG 0x00090 +#define RCB_RING_T0_BE_RST 0x00094 +#define RCB_RING_COULD_BE_RST 0x00098 +#define RCB_RING_WRR_WEIGHT_REG 0x0009c + +#define RCB_RING_INTMSK_RXWL_REG 0x000A0 +#define RCB_RING_INTSTS_RX_RING_REG 0x000A4 +#define RCB_RING_INTMSK_TXWL_REG 0x000AC +#define RCB_RING_INTSTS_TX_RING_REG 0x000B0 +#define RCB_RING_INTMSK_RX_OVERTIME_REG 0x000B8 +#define RCB_RING_INTSTS_RX_OVERTIME_REG 0x000BC +#define RCB_RING_INTMSK_TX_OVERTIME_REG 0x000C4 +#define RCB_RING_INTSTS_TX_OVERTIME_REG 0x000C8 + +#define GMAC_DUPLEX_TYPE_REG 0x0008UL +#define GMAC_FD_FC_TYPE_REG 0x000CUL +#define GMAC_FC_TX_TIMER_REG 0x001CUL +#define GMAC_FD_FC_ADDR_LOW_REG 0x0020UL +#define GMAC_FD_FC_ADDR_HIGH_REG 0x0024UL +#define GMAC_IPG_TX_TIMER_REG 0x0030UL +#define GMAC_PAUSE_THR_REG 0x0038UL +#define GMAC_MAX_FRM_SIZE_REG 0x003CUL +#define GMAC_PORT_MODE_REG 0x0040UL +#define GMAC_PORT_EN_REG 0x0044UL +#define GMAC_PAUSE_EN_REG 0x0048UL +#define GMAC_SHORT_RUNTS_THR_REG 0x0050UL +#define GMAC_AN_NEG_STATE_REG 0x0058UL +#define GMAC_TX_LOCAL_PAGE_REG 0x005CUL +#define GMAC_TRANSMIT_CONTROL_REG 0x0060UL +#define GMAC_REC_FILT_CONTROL_REG 0x0064UL +#define GMAC_PTP_CONFIG_REG 0x0074UL + +#define GMAC_RX_OCTETS_TOTAL_OK_REG 0x0080UL +#define GMAC_RX_OCTETS_BAD_REG 0x0084UL +#define GMAC_RX_UC_PKTS_REG 0x0088UL +#define GMAC_RX_MC_PKTS_REG 0x008CUL +#define GMAC_RX_BC_PKTS_REG 0x0090UL +#define GMAC_RX_PKTS_64OCTETS_REG 0x0094UL +#define GMAC_RX_PKTS_65TO127OCTETS_REG 0x0098UL +#define GMAC_RX_PKTS_128TO255OCTETS_REG 0x009CUL +#define GMAC_RX_PKTS_255TO511OCTETS_REG 0x00A0UL +#define GMAC_RX_PKTS_512TO1023OCTETS_REG 0x00A4UL +#define GMAC_RX_PKTS_1024TO1518OCTETS_REG 0x00A8UL +#define GMAC_RX_PKTS_1519TOMAXOCTETS_REG 0x00ACUL +#define GMAC_RX_FCS_ERRORS_REG 0x00B0UL +#define GMAC_RX_TAGGED_REG 0x00B4UL +#define GMAC_RX_DATA_ERR_REG 0x00B8UL +#define GMAC_RX_ALIGN_ERRORS_REG 0x00BCUL +#define GMAC_RX_LONG_ERRORS_REG 0x00C0UL +#define GMAC_RX_JABBER_ERRORS_REG 0x00C4UL +#define GMAC_RX_PAUSE_MACCTRL_FRAM_REG 0x00C8UL +#define GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG 0x00CCUL +#define GMAC_RX_VERY_LONG_ERR_CNT_REG 0x00D0UL +#define GMAC_RX_RUNT_ERR_CNT_REG 0x00D4UL +#define GMAC_RX_SHORT_ERR_CNT_REG 0x00D8UL +#define GMAC_RX_FILT_PKT_CNT_REG 0x00E8UL +#define GMAC_RX_OCTETS_TOTAL_FILT_REG 0x00ECUL +#define GMAC_OCTETS_TRANSMITTED_OK_REG 0x0100UL +#define GMAC_OCTETS_TRANSMITTED_BAD_REG 0x0104UL +#define GMAC_TX_UC_PKTS_REG 0x0108UL +#define GMAC_TX_MC_PKTS_REG 0x010CUL +#define GMAC_TX_BC_PKTS_REG 0x0110UL +#define GMAC_TX_PKTS_64OCTETS_REG 0x0114UL +#define GMAC_TX_PKTS_65TO127OCTETS_REG 0x0118UL +#define GMAC_TX_PKTS_128TO255OCTETS_REG 0x011CUL +#define GMAC_TX_PKTS_255TO511OCTETS_REG 0x0120UL +#define GMAC_TX_PKTS_512TO1023OCTETS_REG 0x0124UL +#define GMAC_TX_PKTS_1024TO1518OCTETS_REG 0x0128UL +#define GMAC_TX_PKTS_1519TOMAXOCTETS_REG 0x012CUL +#define GMAC_TX_EXCESSIVE_LENGTH_DROP_REG 0x014CUL +#define GMAC_TX_UNDERRUN_REG 0x0150UL +#define GMAC_TX_TAGGED_REG 0x0154UL +#define GMAC_TX_CRC_ERROR_REG 0x0158UL +#define GMAC_TX_PAUSE_FRAMES_REG 0x015CUL +#define GAMC_RX_MAX_FRAME 0x0170UL +#define GMAC_LINE_LOOP_BACK_REG 0x01A8UL +#define GMAC_CF_CRC_STRIP_REG 0x01B0UL +#define GMAC_MODE_CHANGE_EN_REG 0x01B4UL +#define GMAC_SIXTEEN_BIT_CNTR_REG 0x01CCUL +#define GMAC_LD_LINK_COUNTER_REG 0x01D0UL +#define GMAC_LOOP_REG 0x01DCUL +#define GMAC_RECV_CONTROL_REG 0x01E0UL +#define GMAC_VLAN_CODE_REG 0x01E8UL +#define GMAC_RX_OVERRUN_CNT_REG 0x01ECUL +#define GMAC_RX_LENGTHFIELD_ERR_CNT_REG 0x01F4UL +#define GMAC_RX_FAIL_COMMA_CNT_REG 0x01F8UL +#define GMAC_STATION_ADDR_LOW_0_REG 0x0200UL +#define GMAC_STATION_ADDR_HIGH_0_REG 0x0204UL +#define GMAC_STATION_ADDR_LOW_1_REG 0x0208UL +#define GMAC_STATION_ADDR_HIGH_1_REG 0x020CUL +#define GMAC_STATION_ADDR_LOW_2_REG 0x0210UL +#define GMAC_STATION_ADDR_HIGH_2_REG 0x0214UL +#define GMAC_STATION_ADDR_LOW_3_REG 0x0218UL +#define GMAC_STATION_ADDR_HIGH_3_REG 0x021CUL +#define GMAC_STATION_ADDR_LOW_4_REG 0x0220UL +#define GMAC_STATION_ADDR_HIGH_4_REG 0x0224UL +#define GMAC_STATION_ADDR_LOW_5_REG 0x0228UL +#define GMAC_STATION_ADDR_HIGH_5_REG 0x022CUL +#define GMAC_STATION_ADDR_LOW_MSK_0_REG 0x0230UL +#define GMAC_STATION_ADDR_HIGH_MSK_0_REG 0x0234UL +#define GMAC_STATION_ADDR_LOW_MSK_1_REG 0x0238UL +#define GMAC_STATION_ADDR_HIGH_MSK_1_REG 0x023CUL +#define GMAC_MAC_SKIP_LEN_REG 0x0240UL +#define GMAC_TX_LOOP_PKT_PRI_REG 0x0378UL + +#define XGMAC_INT_STATUS_REG 0x0 +#define XGMAC_INT_ENABLE_REG 0x4 +#define XGMAC_INT_SET_REG 0x8 +#define XGMAC_IERR_U_INFO_REG 0xC +#define XGMAC_OVF_INFO_REG 0x10 +#define XGMAC_OVF_CNT_REG 0x14 +#define XGMAC_PORT_MODE_REG 0x40 +#define XGMAC_CLK_ENABLE_REG 0x44 +#define XGMAC_RESET_REG 0x48 +#define XGMAC_LINK_CONTROL_REG 0x50 +#define XGMAC_LINK_STATUS_REG 0x54 +#define XGMAC_SPARE_REG 0xC0 +#define XGMAC_SPARE_CNT_REG 0xC4 + +#define XGMAC_MAC_ENABLE_REG 0x100 +#define XGMAC_MAC_CONTROL_REG 0x104 +#define XGMAC_MAC_IPG_REG 0x120 +#define XGMAC_MAC_MSG_CRC_EN_REG 0x124 +#define XGMAC_MAC_MSG_IMG_REG 0x128 +#define XGMAC_MAC_MSG_FC_CFG_REG 0x12C +#define XGMAC_MAC_MSG_TC_CFG_REG 0x130 +#define XGMAC_MAC_PAD_SIZE_REG 0x134 +#define XGMAC_MAC_MIN_PKT_SIZE_REG 0x138 +#define XGMAC_MAC_MAX_PKT_SIZE_REG 0x13C +#define XGMAC_MAC_PAUSE_CTRL_REG 0x160 +#define XGMAC_MAC_PAUSE_TIME_REG 0x164 +#define XGMAC_MAC_PAUSE_GAP_REG 0x168 +#define XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG 0x16C +#define XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG 0x170 +#define XGMAC_MAC_PAUSE_PEER_MAC_H_REG 0x174 +#define XGMAC_MAC_PAUSE_PEER_MAC_L_REG 0x178 +#define XGMAC_MAC_PFC_PRI_EN_REG 0x17C +#define XGMAC_MAC_1588_CTRL_REG 0x180 +#define XGMAC_MAC_1588_TX_PORT_DLY_REG 0x184 +#define XGMAC_MAC_1588_RX_PORT_DLY_REG 0x188 +#define XGMAC_MAC_1588_ASYM_DLY_REG 0x18C +#define XGMAC_MAC_1588_ADJUST_CFG_REG 0x190 +#define XGMAC_MAC_Y1731_ETH_TYPE_REG 0x194 +#define XGMAC_MAC_MIB_CONTROL_REG 0x198 +#define XGMAC_MAC_WAN_RATE_ADJUST_REG 0x19C +#define XGMAC_MAC_TX_ERR_MARK_REG 0x1A0 +#define XGMAC_MAC_TX_LF_RF_CONTROL_REG 0x1A4 +#define XGMAC_MAC_RX_LF_RF_STATUS_REG 0x1A8 +#define XGMAC_MAC_TX_RUNT_PKT_CNT_REG 0x1C0 +#define XGMAC_MAC_RX_RUNT_PKT_CNT_REG 0x1C4 +#define XGMAC_MAC_RX_PREAM_ERR_PKT_CNT_REG 0x1C8 +#define XGMAC_MAC_TX_LF_RF_TERM_PKT_CNT_REG 0x1CC +#define XGMAC_MAC_TX_SN_MISMATCH_PKT_CNT_REG 0x1D0 +#define XGMAC_MAC_RX_ERR_MSG_CNT_REG 0x1D4 +#define XGMAC_MAC_RX_ERR_EFD_CNT_REG 0x1D8 +#define XGMAC_MAC_ERR_INFO_REG 0x1DC +#define XGMAC_MAC_DBG_INFO_REG 0x1E0 + +#define XGMAC_PCS_BASER_SYNC_THD_REG 0x330 +#define XGMAC_PCS_STATUS1_REG 0x404 +#define XGMAC_PCS_BASER_STATUS1_REG 0x410 +#define XGMAC_PCS_BASER_STATUS2_REG 0x414 +#define XGMAC_PCS_BASER_SEEDA_0_REG 0x420 +#define XGMAC_PCS_BASER_SEEDA_1_REG 0x424 +#define XGMAC_PCS_BASER_SEEDB_0_REG 0x428 +#define XGMAC_PCS_BASER_SEEDB_1_REG 0x42C +#define XGMAC_PCS_BASER_TEST_CONTROL_REG 0x430 +#define XGMAC_PCS_BASER_TEST_ERR_CNT_REG 0x434 +#define XGMAC_PCS_DBG_INFO_REG 0x4C0 +#define XGMAC_PCS_DBG_INFO1_REG 0x4C4 +#define XGMAC_PCS_DBG_INFO2_REG 0x4C8 +#define XGMAC_PCS_DBG_INFO3_REG 0x4CC + +#define XGMAC_PMA_ENABLE_REG 0x700 +#define XGMAC_PMA_CONTROL_REG 0x704 +#define XGMAC_PMA_SIGNAL_STATUS_REG 0x708 +#define XGMAC_PMA_DBG_INFO_REG 0x70C +#define XGMAC_PMA_FEC_ABILITY_REG 0x740 +#define XGMAC_PMA_FEC_CONTROL_REG 0x744 +#define XGMAC_PMA_FEC_CORR_BLOCK_CNT__REG 0x750 +#define XGMAC_PMA_FEC_UNCORR_BLOCK_CNT__REG 0x760 + +#define XGMAC_TX_PKTS_FRAGMENT 0x0000 +#define XGMAC_TX_PKTS_UNDERSIZE 0x0008 +#define XGMAC_TX_PKTS_UNDERMIN 0x0010 +#define XGMAC_TX_PKTS_64OCTETS 0x0018 +#define XGMAC_TX_PKTS_65TO127OCTETS 0x0020 +#define XGMAC_TX_PKTS_128TO255OCTETS 0x0028 +#define XGMAC_TX_PKTS_256TO511OCTETS 0x0030 +#define XGMAC_TX_PKTS_512TO1023OCTETS 0x0038 +#define XGMAC_TX_PKTS_1024TO1518OCTETS 0x0040 +#define XGMAC_TX_PKTS_1519TOMAXOCTETS 0x0048 +#define XGMAC_TX_PKTS_1519TOMAXOCTETSOK 0x0050 +#define XGMAC_TX_PKTS_OVERSIZE 0x0058 +#define XGMAC_TX_PKTS_JABBER 0x0060 +#define XGMAC_TX_GOODPKTS 0x0068 +#define XGMAC_TX_GOODOCTETS 0x0070 +#define XGMAC_TX_TOTAL_PKTS 0x0078 +#define XGMAC_TX_TOTALOCTETS 0x0080 +#define XGMAC_TX_UNICASTPKTS 0x0088 +#define XGMAC_TX_MULTICASTPKTS 0x0090 +#define XGMAC_TX_BROADCASTPKTS 0x0098 +#define XGMAC_TX_PRI0PAUSEPKTS 0x00a0 +#define XGMAC_TX_PRI1PAUSEPKTS 0x00a8 +#define XGMAC_TX_PRI2PAUSEPKTS 0x00b0 +#define XGMAC_TX_PRI3PAUSEPKTS 0x00b8 +#define XGMAC_TX_PRI4PAUSEPKTS 0x00c0 +#define XGMAC_TX_PRI5PAUSEPKTS 0x00c8 +#define XGMAC_TX_PRI6PAUSEPKTS 0x00d0 +#define XGMAC_TX_PRI7PAUSEPKTS 0x00d8 +#define XGMAC_TX_MACCTRLPKTS 0x00e0 +#define XGMAC_TX_1731PKTS 0x00e8 +#define XGMAC_TX_1588PKTS 0x00f0 +#define XGMAC_RX_FROMAPPGOODPKTS 0x00f8 +#define XGMAC_RX_FROMAPPBADPKTS 0x0100 +#define XGMAC_TX_ERRALLPKTS 0x0108 + +#define XGMAC_RX_PKTS_FRAGMENT 0x0110 +#define XGMAC_RX_PKTSUNDERSIZE 0x0118 +#define XGMAC_RX_PKTS_UNDERMIN 0x0120 +#define XGMAC_RX_PKTS_64OCTETS 0x0128 +#define XGMAC_RX_PKTS_65TO127OCTETS 0x0130 +#define XGMAC_RX_PKTS_128TO255OCTETS 0x0138 +#define XGMAC_RX_PKTS_256TO511OCTETS 0x0140 +#define XGMAC_RX_PKTS_512TO1023OCTETS 0x0148 +#define XGMAC_RX_PKTS_1024TO1518OCTETS 0x0150 +#define XGMAC_RX_PKTS_1519TOMAXOCTETS 0x0158 +#define XGMAC_RX_PKTS_1519TOMAXOCTETSOK 0x0160 +#define XGMAC_RX_PKTS_OVERSIZE 0x0168 +#define XGMAC_RX_PKTS_JABBER 0x0170 +#define XGMAC_RX_GOODPKTS 0x0178 +#define XGMAC_RX_GOODOCTETS 0x0180 +#define XGMAC_RX_TOTAL_PKTS 0x0188 +#define XGMAC_RX_TOTALOCTETS 0x0190 +#define XGMAC_RX_UNICASTPKTS 0x0198 +#define XGMAC_RX_MULTICASTPKTS 0x01a0 +#define XGMAC_RX_BROADCASTPKTS 0x01a8 +#define XGMAC_RX_PRI0PAUSEPKTS 0x01b0 +#define XGMAC_RX_PRI1PAUSEPKTS 0x01b8 +#define XGMAC_RX_PRI2PAUSEPKTS 0x01c0 +#define XGMAC_RX_PRI3PAUSEPKTS 0x01c8 +#define XGMAC_RX_PRI4PAUSEPKTS 0x01d0 +#define XGMAC_RX_PRI5PAUSEPKTS 0x01d8 +#define XGMAC_RX_PRI6PAUSEPKTS 0x01e0 +#define XGMAC_RX_PRI7PAUSEPKTS 0x01e8 +#define XGMAC_RX_MACCTRLPKTS 0x01f0 +#define XGMAC_TX_SENDAPPGOODPKTS 0x01f8 +#define XGMAC_TX_SENDAPPBADPKTS 0x0200 +#define XGMAC_RX_1731PKTS 0x0208 +#define XGMAC_RX_SYMBOLERRPKTS 0x0210 +#define XGMAC_RX_FCSERRPKTS 0x0218 + +#define XGMAC_TRX_CORE_SRST_M 0x2080 + +#define DSAF_CFG_EN_S 0 +#define DSAF_CFG_TC_MODE_S 1 +#define DSAF_CFG_CRC_EN_S 2 +#define DSAF_CFG_SBM_INIT_S 3 +#define DSAF_CFG_MIX_MODE_S 4 +#define DSAF_CFG_STP_MODE_S 5 +#define DSAF_CFG_LOCA_ADDR_EN_S 6 + +#define DSAF_CNT_CLR_CE_S 0 +#define DSAF_SNAP_EN_S 1 + +#define HNS_DSAF_PFC_UNIT_CNT_FOR_XGE 41 +#define HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000 410 +#define HNS_DSAF_PFC_UNIT_CNT_FOR_GE_2500 103 + +#define DSAF_PFC_UNINT_CNT_M ((1ULL << 9) - 1) +#define DSAF_PFC_UNINT_CNT_S 0 + +#define DSAF_PPE_QID_CFG_M 0xFF +#define DSAF_PPE_QID_CFG_S 0 + +#define DSAF_SW_PORT_TYPE_M 3 +#define DSAF_SW_PORT_TYPE_S 0 + +#define DSAF_STP_PORT_TYPE_M 7 +#define DSAF_STP_PORT_TYPE_S 0 + +#define DSAF_INODE_IN_PORT_NUM_M 7 +#define DSAF_INODE_IN_PORT_NUM_S 0 + +#define HNS_DSAF_I4TC_CFG 0x18688688 +#define HNS_DSAF_I8TC_CFG 0x18FAC688 + +#define DSAF_SBM_CFG_SHCUT_EN_S 0 +#define DSAF_SBM_CFG_EN_S 1 +#define DSAF_SBM_CFG_MIB_EN_S 2 +#define DSAF_SBM_CFG_ECC_INVERT_EN_S 3 + +#define DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_S 0 +#define DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_S 10 +#define DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 10) +#define DSAF_SBM_CFG0_COM_MAX_BUF_NUM_S 20 +#define DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M (((1ULL << 11) - 1) << 20) + +#define DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_S 0 +#define DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_S 10 +#define DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 10) + +#define DSAF_SBM_CFG2_SET_BUF_NUM_S 0 +#define DSAF_SBM_CFG2_SET_BUF_NUM_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG2_RESET_BUF_NUM_S 10 +#define DSAF_SBM_CFG2_RESET_BUF_NUM_M (((1ULL << 10) - 1) << 10) + +#define DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S 0 +#define DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S 10 +#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M (((1ULL << 10) - 1) << 10) + +#define DSAF_TBL_TCAM_ADDR_S 0 +#define DSAF_TBL_TCAM_ADDR_M ((1ULL << 9) - 1) + +#define DSAF_TBL_LINE_ADDR_S 0 +#define DSAF_TBL_LINE_ADDR_M ((1ULL << 15) - 1) + +#define DSAF_TBL_MCAST_CFG4_VM128_112_S 0 +#define DSAF_TBL_MCAST_CFG4_VM128_112_M (((1ULL << 7) - 1) << 0) +#define DSAF_TBL_MCAST_CFG4_ITEM_VLD_S 7 +#define DSAF_TBL_MCAST_CFG4_OLD_EN_S 8 + +#define DSAF_TBL_MCAST_CFG0_XGE5_0_S 0 +#define DSAF_TBL_MCAST_CFG0_XGE5_0_M (((1ULL << 6) - 1) << 0) +#define DSAF_TBL_MCAST_CFG0_VM25_0_S 6 +#define DSAF_TBL_MCAST_CFG0_VM25_0_M (((1ULL << 26) - 1) << 6) + +#define DSAF_TBL_UCAST_CFG1_OUT_PORT_S 0 +#define DSAF_TBL_UCAST_CFG1_OUT_PORT_M (((1ULL << 8) - 1) << 0) +#define DSAF_TBL_UCAST_CFG1_DVC_S 8 +#define DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S 9 +#define DSAF_TBL_UCAST_CFG1_ITEM_VLD_S 10 +#define DSAF_TBL_UCAST_CFG1_OLD_EN_S 11 + +#define DSAF_TBL_LINE_CFG_OUT_PORT_S 0 +#define DSAF_TBL_LINE_CFG_OUT_PORT_M (((1ULL << 8) - 1) << 0) +#define DSAF_TBL_LINE_CFG_DVC_S 8 +#define DSAF_TBL_LINE_CFG_MAC_DISCARD_S 9 + +#define DSAF_TBL_PUL_OLD_RSLT_RE_S 0 +#define DSAF_TBL_PUL_MCAST_VLD_S 1 +#define DSAF_TBL_PUL_TCAM_DATA_VLD_S 2 +#define DSAF_TBL_PUL_UCAST_VLD_S 3 +#define DSAF_TBL_PUL_LINE_VLD_S 4 +#define DSAF_TBL_PUL_TCAM_LOAD_S 5 +#define DSAF_TBL_PUL_LINE_LOAD_S 6 + +#define DSAF_TBL_DFX_LINE_LKUP_NUM_EN_S 0 +#define DSAF_TBL_DFX_UC_LKUP_NUM_EN_S 1 +#define DSAF_TBL_DFX_MC_LKUP_NUM_EN_S 2 +#define DSAF_TBL_DFX_BC_LKUP_NUM_EN_S 3 +#define DSAF_TBL_DFX_RAM_ERR_INJECT_EN_S 4 + +#define DSAF_VOQ_BP_ALL_DOWNTHRD_S 0 +#define DSAF_VOQ_BP_ALL_DOWNTHRD_M (((1ULL << 10) - 1) << 0) +#define DSAF_VOQ_BP_ALL_UPTHRD_S 10 +#define DSAF_VOQ_BP_ALL_UPTHRD_M (((1ULL << 10) - 1) << 10) + +#define DSAF_XGE_GE_WORK_MODE_S 0 +#define DSAF_XGE_GE_LOOPBACK_S 1 + +#define DSAF_FC_XGE_TX_PAUSE_S 0 +#define DSAF_REGS_XGE_CNT_CAR_S 1 + +#define PPE_CFG_QID_MODE_DEF_QID_S 0 +#define PPE_CFG_QID_MODE_DEF_QID_M (0xff << PPE_CFG_QID_MODE_DEF_QID_S) + +#define PPE_CFG_QID_MODE_CF_QID_MODE_S 8 +#define PPE_CFG_QID_MODE_CF_QID_MODE_M (0x7 << PPE_CFG_QID_MODE_CF_QID_MODE_S) + +#define PPE_CNT_CLR_CE_B 0 +#define PPE_CNT_CLR_SNAP_EN_B 1 + +#define PPE_COMMON_CNT_CLR_CE_B 0 +#define PPE_COMMON_CNT_CLR_SNAP_EN_B 1 + +#define GMAC_DUPLEX_TYPE_B 0 + +#define GMAC_FC_TX_TIMER_S 0 +#define GMAC_FC_TX_TIMER_M 0xffff + +#define GMAC_MAX_FRM_SIZE_S 0 +#define GMAC_MAX_FRM_SIZE_M 0xffff + +#define GMAC_PORT_MODE_S 0 +#define GMAC_PORT_MODE_M 0xf + +#define GMAC_RGMII_1000M_DELAY_B 4 +#define GMAC_MII_TX_EDGE_SEL_B 5 +#define GMAC_FIFO_ERR_AUTO_RST_B 6 +#define GMAC_DBG_CLK_LOS_MSK_B 7 + +#define GMAC_PORT_RX_EN_B 1 +#define GMAC_PORT_TX_EN_B 2 + +#define GMAC_PAUSE_EN_RX_FDFC_B 0 +#define GMAC_PAUSE_EN_TX_FDFC_B 1 +#define GMAC_PAUSE_EN_TX_HDFC_B 2 + +#define GMAC_SHORT_RUNTS_THR_S 0 +#define GMAC_SHORT_RUNTS_THR_M 0x1f + +#define GMAC_AN_NEG_STAT_FD_B 5 +#define GMAC_AN_NEG_STAT_HD_B 6 +#define GMAC_AN_NEG_STAT_RF1_DUPLIEX_B 12 +#define GMAC_AN_NEG_STAT_RF2_B 13 + +#define GMAC_AN_NEG_STAT_NP_LNK_OK_B 15 +#define GMAC_AN_NEG_STAT_RX_SYNC_OK_B 20 +#define GMAC_AN_NEG_STAT_AN_DONE_B 21 + +#define GMAC_AN_NEG_STAT_PS_S 7 +#define GMAC_AN_NEG_STAT_PS_M (0x3 << GMAC_AN_NEG_STAT_PS_S) + +#define GMAC_AN_NEG_STAT_SPEED_S 10 +#define GMAC_AN_NEG_STAT_SPEED_M (0x3 << GMAC_AN_NEG_STAT_SPEED_S) + +#define GMAC_TX_AN_EN_B 5 +#define GMAC_TX_CRC_ADD_B 6 +#define GMAC_TX_PAD_EN_B 7 + +#define GMAC_LINE_LOOPBACK_B 0 + +#define GMAC_LP_REG_CF_EXT_DRV_LP_B 1 +#define GMAC_LP_REG_CF2MI_LP_EN_B 2 + +#define GMAC_MODE_CHANGE_EB_B 0 + +#define GMAC_RECV_CTRL_STRIP_PAD_EN_B 3 +#define GMAC_RECV_CTRL_RUNT_PKT_EN_B 4 + +#define GMAC_TX_LOOP_PKT_HIG_PRI_B 0 +#define GMAC_TX_LOOP_PKT_EN_B 1 + +#define XGMAC_PORT_MODE_TX_S 0x0 +#define XGMAC_PORT_MODE_TX_M (0x3 << XGMAC_PORT_MODE_TX_S) +#define XGMAC_PORT_MODE_TX_40G_B 0x3 +#define XGMAC_PORT_MODE_RX_S 0x4 +#define XGMAC_PORT_MODE_RX_M (0x3 << XGMAC_PORT_MODE_RX_S) +#define XGMAC_PORT_MODE_RX_40G_B 0x7 + +#define XGMAC_ENABLE_TX_B 0 +#define XGMAC_ENABLE_RX_B 1 + +#define XGMAC_CTL_TX_FCS_B 0 +#define XGMAC_CTL_TX_PAD_B 1 +#define XGMAC_CTL_TX_PREAMBLE_TRANS_B 3 +#define XGMAC_CTL_TX_UNDER_MIN_ERR_B 4 +#define XGMAC_CTL_TX_TRUNCATE_B 5 +#define XGMAC_CTL_TX_1588_B 8 +#define XGMAC_CTL_TX_1731_B 9 +#define XGMAC_CTL_TX_PFC_B 10 +#define XGMAC_CTL_RX_FCS_B 16 +#define XGMAC_CTL_RX_FCS_STRIP_B 17 +#define XGMAC_CTL_RX_PREAMBLE_TRANS_B 19 +#define XGMAC_CTL_RX_UNDER_MIN_ERR_B 20 +#define XGMAC_CTL_RX_TRUNCATE_B 21 +#define XGMAC_CTL_RX_1588_B 24 +#define XGMAC_CTL_RX_1731_B 25 +#define XGMAC_CTL_RX_PFC_B 26 + +#define XGMAC_PMA_FEC_CTL_TX_B 0 +#define XGMAC_PMA_FEC_CTL_RX_B 1 +#define XGMAC_PMA_FEC_CTL_ERR_EN 2 +#define XGMAC_PMA_FEC_CTL_ERR_SH 3 + +#define XGMAC_PAUSE_CTL_TX_B 0 +#define XGMAC_PAUSE_CTL_RX_B 1 +#define XGMAC_PAUSE_CTL_RSP_MODE_B 2 +#define XGMAC_PAUSE_CTL_TX_XOFF_B 3 + +static inline void dsaf_write_reg(void *base, u32 reg, u32 value) +{ + u8 __iomem *reg_addr = ACCESS_ONCE(base); + + writel(value, reg_addr + reg); +} + +#define dsaf_write_dev(a, reg, value) \ + dsaf_write_reg((a)->io_base, (reg), (value)) + +static inline u32 dsaf_read_reg(u8 *base, u32 reg) +{ + u8 __iomem *reg_addr = ACCESS_ONCE(base); + + return readl(reg_addr + reg); +} + +#define dsaf_read_dev(a, reg) \ + dsaf_read_reg((a)->io_base, (reg)) + +#define dsaf_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~(mask)); \ + (origin) |= (((val) << (shift)) & (mask)); \ + } while (0) + +#define dsaf_set_bit(origin, shift, val) \ + dsaf_set_field((origin), (1ull << (shift)), (shift), (val)) + +static inline void dsaf_set_reg_field(void *base, u32 reg, u32 mask, u32 shift, + u32 val) +{ + u32 origin = dsaf_read_reg(base, reg); + + dsaf_set_field(origin, mask, shift, val); + dsaf_write_reg(base, reg, origin); +} + +#define dsaf_set_dev_field(dev, reg, mask, shift, val) \ + dsaf_set_reg_field((dev)->io_base, (reg), (mask), (shift), (val)) + +#define dsaf_set_dev_bit(dev, reg, bit, val) \ + dsaf_set_reg_field((dev)->io_base, (reg), (1ull << (bit)), (bit), (val)) + +#define dsaf_get_field(origin, mask, shift) (((origin) & (mask)) >> (shift)) + +#define dsaf_get_bit(origin, shift) \ + dsaf_get_field((origin), (1ull << (shift)), (shift)) + +static inline u32 dsaf_get_reg_field(void *base, u32 reg, u32 mask, u32 shift) +{ + u32 origin; + + origin = dsaf_read_reg(base, reg); + return dsaf_get_field(origin, mask, shift); +} + +#define dsaf_get_dev_field(dev, reg, mask, shift) \ + dsaf_get_reg_field((dev)->io_base, (reg), (mask), (shift)) + +#define dsaf_get_dev_bit(dev, reg, bit) \ + dsaf_get_reg_field((dev)->io_base, (reg), (1ull << (bit)), (bit)) + +#define dsaf_write_b(addr, data)\ + writeb((data), (__iomem unsigned char *)(addr)) +#define dsaf_read_b(addr)\ + readb((__iomem unsigned char *)(addr)) + +#define hns_mac_reg_read64(drv, offset) \ + readq((__iomem void *)(((u8 *)(drv)->io_base + 0xc00 + (offset)))) + +#endif /* _DSAF_REG_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c new file mode 100644 index 000000000000..dab5ecf382a0 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c @@ -0,0 +1,837 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <asm-generic/io-64-nonatomic-hi-lo.h> +#include <linux/of_mdio.h> +#include "hns_dsaf_main.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_xgmac.h" +#include "hns_dsaf_reg.h" + +static const struct mac_stats_string g_xgmac_stats_string[] = { + {"xgmac_tx_bad_pkts_minto64", MAC_STATS_FIELD_OFF(tx_fragment_err)}, + {"xgmac_tx_good_pkts_minto64", MAC_STATS_FIELD_OFF(tx_undersize)}, + {"xgmac_tx_total_pkts_minto64", MAC_STATS_FIELD_OFF(tx_under_min_pkts)}, + {"xgmac_tx_pkts_64", MAC_STATS_FIELD_OFF(tx_64bytes)}, + {"xgmac_tx_pkts_65to127", MAC_STATS_FIELD_OFF(tx_65to127)}, + {"xgmac_tx_pkts_128to255", MAC_STATS_FIELD_OFF(tx_128to255)}, + {"xgmac_tx_pkts_256to511", MAC_STATS_FIELD_OFF(tx_256to511)}, + {"xgmac_tx_pkts_512to1023", MAC_STATS_FIELD_OFF(tx_512to1023)}, + {"xgmac_tx_pkts_1024to1518", MAC_STATS_FIELD_OFF(tx_1024to1518)}, + {"xgmac_tx_pkts_1519tomax", MAC_STATS_FIELD_OFF(tx_1519tomax)}, + {"xgmac_tx_good_pkts_1519tomax", + MAC_STATS_FIELD_OFF(tx_1519tomax_good)}, + {"xgmac_tx_good_pkts_untralmax", MAC_STATS_FIELD_OFF(tx_oversize)}, + {"xgmac_tx_bad_pkts_untralmax", MAC_STATS_FIELD_OFF(tx_jabber_err)}, + {"xgmac_tx_good_pkts_all", MAC_STATS_FIELD_OFF(tx_good_pkts)}, + {"xgmac_tx_good_byte_all", MAC_STATS_FIELD_OFF(tx_good_bytes)}, + {"xgmac_tx_total_pkt", MAC_STATS_FIELD_OFF(tx_total_pkts)}, + {"xgmac_tx_total_byt", MAC_STATS_FIELD_OFF(tx_total_bytes)}, + {"xgmac_tx_uc_pkt", MAC_STATS_FIELD_OFF(tx_uc_pkts)}, + {"xgmac_tx_mc_pkt", MAC_STATS_FIELD_OFF(tx_mc_pkts)}, + {"xgmac_tx_bc_pkt", MAC_STATS_FIELD_OFF(tx_bc_pkts)}, + {"xgmac_tx_pause_frame_num", MAC_STATS_FIELD_OFF(tx_pfc_tc0)}, + {"xgmac_tx_pfc_per_1pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc1)}, + {"xgmac_tx_pfc_per_2pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc2)}, + {"xgmac_tx_pfc_per_3pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc3)}, + {"xgmac_tx_pfc_per_4pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc4)}, + {"xgmac_tx_pfc_per_5pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc5)}, + {"xgmac_tx_pfc_per_6pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc6)}, + {"xgmac_tx_pfc_per_7pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc7)}, + {"xgmac_tx_mac_ctrol_frame", MAC_STATS_FIELD_OFF(tx_ctrl)}, + {"xgmac_tx_1731_pkts", MAC_STATS_FIELD_OFF(tx_1731_pkts)}, + {"xgmac_tx_1588_pkts", MAC_STATS_FIELD_OFF(tx_1588_pkts)}, + {"xgmac_rx_good_pkt_from_dsaf", MAC_STATS_FIELD_OFF(rx_good_from_sw)}, + {"xgmac_rx_bad_pkt_from_dsaf", MAC_STATS_FIELD_OFF(rx_bad_from_sw)}, + {"xgmac_tx_bad_pkt_64tomax", MAC_STATS_FIELD_OFF(tx_bad_pkts)}, + + {"xgmac_rx_not_well_pkt", MAC_STATS_FIELD_OFF(rx_fragment_err)}, + {"xgmac_rx_good_well_pkt", MAC_STATS_FIELD_OFF(rx_undersize)}, + {"xgmac_rx_total_pkt", MAC_STATS_FIELD_OFF(rx_under_min)}, + {"xgmac_rx_pkt_64", MAC_STATS_FIELD_OFF(rx_64bytes)}, + {"xgmac_rx_pkt_65to127", MAC_STATS_FIELD_OFF(rx_65to127)}, + {"xgmac_rx_pkt_128to255", MAC_STATS_FIELD_OFF(rx_128to255)}, + {"xgmac_rx_pkt_256to511", MAC_STATS_FIELD_OFF(rx_256to511)}, + {"xgmac_rx_pkt_512to1023", MAC_STATS_FIELD_OFF(rx_512to1023)}, + {"xgmac_rx_pkt_1024to1518", MAC_STATS_FIELD_OFF(rx_1024to1518)}, + {"xgmac_rx_pkt_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax)}, + {"xgmac_rx_good_pkt_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax_good)}, + {"xgmac_rx_good_pkt_untramax", MAC_STATS_FIELD_OFF(rx_oversize)}, + {"xgmac_rx_bad_pkt_untramax", MAC_STATS_FIELD_OFF(rx_jabber_err)}, + {"xgmac_rx_good_pkt", MAC_STATS_FIELD_OFF(rx_good_pkts)}, + {"xgmac_rx_good_byt", MAC_STATS_FIELD_OFF(rx_good_bytes)}, + {"xgmac_rx_pkt", MAC_STATS_FIELD_OFF(rx_total_pkts)}, + {"xgmac_rx_byt", MAC_STATS_FIELD_OFF(rx_total_bytes)}, + {"xgmac_rx_uc_pkt", MAC_STATS_FIELD_OFF(rx_uc_pkts)}, + {"xgmac_rx_mc_pkt", MAC_STATS_FIELD_OFF(rx_mc_pkts)}, + {"xgmac_rx_bc_pkt", MAC_STATS_FIELD_OFF(rx_bc_pkts)}, + {"xgmac_rx_pause_frame_num", MAC_STATS_FIELD_OFF(rx_pfc_tc0)}, + {"xgmac_rx_pfc_per_1pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc1)}, + {"xgmac_rx_pfc_per_2pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc2)}, + {"xgmac_rx_pfc_per_3pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc3)}, + {"xgmac_rx_pfc_per_4pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc4)}, + {"xgmac_rx_pfc_per_5pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc5)}, + {"xgmac_rx_pfc_per_6pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc6)}, + {"xgmac_rx_pfc_per_7pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc7)}, + {"xgmac_rx_mac_control", MAC_STATS_FIELD_OFF(rx_unknown_ctrl)}, + {"xgmac_tx_good_pkt_todsaf", MAC_STATS_FIELD_OFF(tx_good_to_sw)}, + {"xgmac_tx_bad_pkt_todsaf", MAC_STATS_FIELD_OFF(tx_bad_to_sw)}, + {"xgmac_rx_1731_pkt", MAC_STATS_FIELD_OFF(rx_1731_pkts)}, + {"xgmac_rx_symbol_err_pkt", MAC_STATS_FIELD_OFF(rx_symbol_err)}, + {"xgmac_rx_fcs_pkt", MAC_STATS_FIELD_OFF(rx_fcs_err)} +}; + +/** + *hns_xgmac_tx_enable - xgmac port tx enable + *@drv: mac driver + *@value: value of enable + */ +static void hns_xgmac_tx_enable(struct mac_driver *drv, u32 value) +{ + dsaf_set_dev_bit(drv, XGMAC_MAC_ENABLE_REG, XGMAC_ENABLE_TX_B, !!value); +} + +/** + *hns_xgmac_rx_enable - xgmac port rx enable + *@drv: mac driver + *@value: value of enable + */ +static void hns_xgmac_rx_enable(struct mac_driver *drv, u32 value) +{ + dsaf_set_dev_bit(drv, XGMAC_MAC_ENABLE_REG, XGMAC_ENABLE_RX_B, !!value); +} + +/** + *hns_xgmac_enable - enable xgmac port + *@drv: mac driver + *@mode: mode of mac port + */ +static void hns_xgmac_enable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + u32 port = drv->mac_id; + + hns_dsaf_xge_core_srst_by_port(dsaf_dev, port, 1); + mdelay(10); + + /*enable XGE rX/tX */ + if (mode == MAC_COMM_MODE_TX) { + hns_xgmac_tx_enable(drv, 1); + } else if (mode == MAC_COMM_MODE_RX) { + hns_xgmac_rx_enable(drv, 1); + } else if (mode == MAC_COMM_MODE_RX_AND_TX) { + hns_xgmac_tx_enable(drv, 1); + hns_xgmac_rx_enable(drv, 1); + } else { + dev_err(drv->dev, "error mac mode:%d\n", mode); + } +} + +/** + *hns_xgmac_disable - disable xgmac port + *@mac_drv: mac driver + *@mode: mode of mac port + */ +static void hns_xgmac_disable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + u32 port = drv->mac_id; + + if (mode == MAC_COMM_MODE_TX) { + hns_xgmac_tx_enable(drv, 0); + } else if (mode == MAC_COMM_MODE_RX) { + hns_xgmac_rx_enable(drv, 0); + } else if (mode == MAC_COMM_MODE_RX_AND_TX) { + hns_xgmac_tx_enable(drv, 0); + hns_xgmac_rx_enable(drv, 0); + } + + mdelay(10); + hns_dsaf_xge_core_srst_by_port(dsaf_dev, port, 0); +} + +/** + *hns_xgmac_pma_fec_enable - xgmac PMA FEC enable + *@drv: mac driver + *@tx_value: tx value + *@rx_value: rx value + *return status + */ +static void hns_xgmac_pma_fec_enable(struct mac_driver *drv, u32 tx_value, + u32 rx_value) +{ + u32 origin = dsaf_read_dev(drv, XGMAC_PMA_FEC_CONTROL_REG); + + dsaf_set_bit(origin, XGMAC_PMA_FEC_CTL_TX_B, !!tx_value); + dsaf_set_bit(origin, XGMAC_PMA_FEC_CTL_RX_B, !!rx_value); + dsaf_write_dev(drv, XGMAC_PMA_FEC_CONTROL_REG, origin); +} + +/* clr exc irq for xge*/ +static void hns_xgmac_exc_irq_en(struct mac_driver *drv, u32 en) +{ + u32 clr_vlue = 0xfffffffful; + u32 msk_vlue = en ? 0xfffffffful : 0; /*1 is en, 0 is dis*/ + + dsaf_write_dev(drv, XGMAC_INT_STATUS_REG, clr_vlue); + dsaf_write_dev(drv, XGMAC_INT_ENABLE_REG, msk_vlue); +} + +/** + *hns_xgmac_init - initialize XGE + *@mac_drv: mac driver + */ +static void hns_xgmac_init(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + u32 port = drv->mac_id; + + hns_dsaf_xge_srst_by_port(dsaf_dev, port, 0); + mdelay(100); + hns_dsaf_xge_srst_by_port(dsaf_dev, port, 1); + + mdelay(100); + hns_xgmac_exc_irq_en(drv, 0); + + hns_xgmac_pma_fec_enable(drv, 0x0, 0x0); + + hns_xgmac_disable(mac_drv, MAC_COMM_MODE_RX_AND_TX); +} + +/** + *hns_xgmac_config_pad_and_crc - set xgmac pad and crc enable the same time + *@mac_drv: mac driver + *@newval:enable of pad and crc + */ +static void hns_xgmac_config_pad_and_crc(void *mac_drv, u8 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 origin = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG); + + dsaf_set_bit(origin, XGMAC_CTL_TX_PAD_B, !!newval); + dsaf_set_bit(origin, XGMAC_CTL_TX_FCS_B, !!newval); + dsaf_set_bit(origin, XGMAC_CTL_RX_FCS_B, !!newval); + dsaf_write_dev(drv, XGMAC_MAC_CONTROL_REG, origin); +} + +/** + *hns_xgmac_pausefrm_cfg - set pause param about xgmac + *@mac_drv: mac driver + *@newval:enable of pad and crc + */ +static void hns_xgmac_pausefrm_cfg(void *mac_drv, u32 rx_en, u32 tx_en) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 origin = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + + dsaf_set_bit(origin, XGMAC_PAUSE_CTL_TX_B, !!tx_en); + dsaf_set_bit(origin, XGMAC_PAUSE_CTL_RX_B, !!rx_en); + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG, origin); +} + +static void hns_xgmac_set_pausefrm_mac_addr(void *mac_drv, char *mac_addr) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + u32 high_val = mac_addr[1] | (mac_addr[0] << 8); + u32 low_val = mac_addr[5] | (mac_addr[4] << 8) + | (mac_addr[3] << 16) | (mac_addr[2] << 24); + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG, low_val); + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG, high_val); +} + +/** + *hns_xgmac_set_rx_ignore_pause_frames - set rx pause param about xgmac + *@mac_drv: mac driver + *@enable:enable rx pause param + */ +static void hns_xgmac_set_rx_ignore_pause_frames(void *mac_drv, u32 enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, XGMAC_MAC_PAUSE_CTRL_REG, + XGMAC_PAUSE_CTL_RX_B, !!enable); +} + +/** + *hns_xgmac_set_tx_auto_pause_frames - set tx pause param about xgmac + *@mac_drv: mac driver + *@enable:enable tx pause param + */ +static void hns_xgmac_set_tx_auto_pause_frames(void *mac_drv, u16 enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, XGMAC_MAC_PAUSE_CTRL_REG, + XGMAC_PAUSE_CTL_TX_B, !!enable); + + /*if enable is not zero ,set tx pause time */ + if (enable) + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_TIME_REG, enable); +} + +/** + *hns_xgmac_get_id - get xgmac port id + *@mac_drv: mac driver + *@newval:xgmac max frame length + */ +static void hns_xgmac_get_id(void *mac_drv, u8 *mac_id) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *mac_id = drv->mac_id; +} + +/** + *hns_xgmac_config_max_frame_length - set xgmac max frame length + *@mac_drv: mac driver + *@newval:xgmac max frame length + */ +static void hns_xgmac_config_max_frame_length(void *mac_drv, u16 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_write_dev(drv, XGMAC_MAC_MAX_PKT_SIZE_REG, newval); +} + +void hns_xgmac_update_stats(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct mac_hw_stats *hw_stats = &drv->mac_cb->hw_stats; + + /* TX */ + hw_stats->tx_fragment_err + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_FRAGMENT); + hw_stats->tx_undersize + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERSIZE); + hw_stats->tx_under_min_pkts + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERMIN); + hw_stats->tx_64bytes = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_64OCTETS); + hw_stats->tx_65to127 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_65TO127OCTETS); + hw_stats->tx_128to255 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_128TO255OCTETS); + hw_stats->tx_256to511 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_256TO511OCTETS); + hw_stats->tx_512to1023 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_512TO1023OCTETS); + hw_stats->tx_1024to1518 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1024TO1518OCTETS); + hw_stats->tx_1519tomax + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETS); + hw_stats->tx_1519tomax_good + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETSOK); + hw_stats->tx_oversize = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_OVERSIZE); + hw_stats->tx_jabber_err = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_JABBER); + hw_stats->tx_good_pkts = hns_mac_reg_read64(drv, XGMAC_TX_GOODPKTS); + hw_stats->tx_good_bytes = hns_mac_reg_read64(drv, XGMAC_TX_GOODOCTETS); + hw_stats->tx_total_pkts = hns_mac_reg_read64(drv, XGMAC_TX_TOTAL_PKTS); + hw_stats->tx_total_bytes + = hns_mac_reg_read64(drv, XGMAC_TX_TOTALOCTETS); + hw_stats->tx_uc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_UNICASTPKTS); + hw_stats->tx_mc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_MULTICASTPKTS); + hw_stats->tx_bc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_BROADCASTPKTS); + hw_stats->tx_pfc_tc0 = hns_mac_reg_read64(drv, XGMAC_TX_PRI0PAUSEPKTS); + hw_stats->tx_pfc_tc1 = hns_mac_reg_read64(drv, XGMAC_TX_PRI1PAUSEPKTS); + hw_stats->tx_pfc_tc2 = hns_mac_reg_read64(drv, XGMAC_TX_PRI2PAUSEPKTS); + hw_stats->tx_pfc_tc3 = hns_mac_reg_read64(drv, XGMAC_TX_PRI3PAUSEPKTS); + hw_stats->tx_pfc_tc4 = hns_mac_reg_read64(drv, XGMAC_TX_PRI4PAUSEPKTS); + hw_stats->tx_pfc_tc5 = hns_mac_reg_read64(drv, XGMAC_TX_PRI5PAUSEPKTS); + hw_stats->tx_pfc_tc6 = hns_mac_reg_read64(drv, XGMAC_TX_PRI6PAUSEPKTS); + hw_stats->tx_pfc_tc7 = hns_mac_reg_read64(drv, XGMAC_TX_PRI7PAUSEPKTS); + hw_stats->tx_ctrl = hns_mac_reg_read64(drv, XGMAC_TX_MACCTRLPKTS); + hw_stats->tx_1731_pkts = hns_mac_reg_read64(drv, XGMAC_TX_1731PKTS); + hw_stats->tx_1588_pkts = hns_mac_reg_read64(drv, XGMAC_TX_1588PKTS); + hw_stats->rx_good_from_sw + = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPGOODPKTS); + hw_stats->rx_bad_from_sw + = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPBADPKTS); + hw_stats->tx_bad_pkts = hns_mac_reg_read64(drv, XGMAC_TX_ERRALLPKTS); + + /* RX */ + hw_stats->rx_fragment_err + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_FRAGMENT); + hw_stats->rx_undersize + = hns_mac_reg_read64(drv, XGMAC_RX_PKTSUNDERSIZE); + hw_stats->rx_under_min + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_UNDERMIN); + hw_stats->rx_64bytes = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_64OCTETS); + hw_stats->rx_65to127 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_65TO127OCTETS); + hw_stats->rx_128to255 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_128TO255OCTETS); + hw_stats->rx_256to511 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_256TO511OCTETS); + hw_stats->rx_512to1023 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_512TO1023OCTETS); + hw_stats->rx_1024to1518 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1024TO1518OCTETS); + hw_stats->rx_1519tomax + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETS); + hw_stats->rx_1519tomax_good + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETSOK); + hw_stats->rx_oversize = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_OVERSIZE); + hw_stats->rx_jabber_err = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_JABBER); + hw_stats->rx_good_pkts = hns_mac_reg_read64(drv, XGMAC_RX_GOODPKTS); + hw_stats->rx_good_bytes = hns_mac_reg_read64(drv, XGMAC_RX_GOODOCTETS); + hw_stats->rx_total_pkts = hns_mac_reg_read64(drv, XGMAC_RX_TOTAL_PKTS); + hw_stats->rx_total_bytes + = hns_mac_reg_read64(drv, XGMAC_RX_TOTALOCTETS); + hw_stats->rx_uc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_UNICASTPKTS); + hw_stats->rx_mc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_MULTICASTPKTS); + hw_stats->rx_bc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_BROADCASTPKTS); + hw_stats->rx_pfc_tc0 = hns_mac_reg_read64(drv, XGMAC_RX_PRI0PAUSEPKTS); + hw_stats->rx_pfc_tc1 = hns_mac_reg_read64(drv, XGMAC_RX_PRI1PAUSEPKTS); + hw_stats->rx_pfc_tc2 = hns_mac_reg_read64(drv, XGMAC_RX_PRI2PAUSEPKTS); + hw_stats->rx_pfc_tc3 = hns_mac_reg_read64(drv, XGMAC_RX_PRI3PAUSEPKTS); + hw_stats->rx_pfc_tc4 = hns_mac_reg_read64(drv, XGMAC_RX_PRI4PAUSEPKTS); + hw_stats->rx_pfc_tc5 = hns_mac_reg_read64(drv, XGMAC_RX_PRI5PAUSEPKTS); + hw_stats->rx_pfc_tc6 = hns_mac_reg_read64(drv, XGMAC_RX_PRI6PAUSEPKTS); + hw_stats->rx_pfc_tc7 = hns_mac_reg_read64(drv, XGMAC_RX_PRI7PAUSEPKTS); + + hw_stats->rx_unknown_ctrl + = hns_mac_reg_read64(drv, XGMAC_RX_MACCTRLPKTS); + hw_stats->tx_good_to_sw + = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPGOODPKTS); + hw_stats->tx_bad_to_sw + = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPBADPKTS); + hw_stats->rx_1731_pkts = hns_mac_reg_read64(drv, XGMAC_RX_1731PKTS); + hw_stats->rx_symbol_err + = hns_mac_reg_read64(drv, XGMAC_RX_SYMBOLERRPKTS); + hw_stats->rx_fcs_err = hns_mac_reg_read64(drv, XGMAC_RX_FCSERRPKTS); +} + +/** + *hns_xgmac_free - free xgmac driver + *@mac_drv: mac driver + */ +static void hns_xgmac_free(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + + u32 mac_id = drv->mac_id; + + hns_dsaf_xge_srst_by_port(dsaf_dev, mac_id, 0); +} + +/** + *hns_xgmac_get_info - get xgmac information + *@mac_drv: mac driver + *@mac_info:mac information + */ +static void hns_xgmac_get_info(void *mac_drv, struct mac_info *mac_info) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 pause_time, pause_ctrl, port_mode, ctrl_val; + + ctrl_val = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG); + mac_info->pad_and_crc_en = dsaf_get_bit(ctrl_val, XGMAC_CTL_TX_PAD_B); + mac_info->auto_neg = 0; + + pause_time = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_TIME_REG); + mac_info->tx_pause_time = pause_time; + + port_mode = dsaf_read_dev(drv, XGMAC_PORT_MODE_REG); + mac_info->port_en = dsaf_get_field(port_mode, XGMAC_PORT_MODE_TX_M, + XGMAC_PORT_MODE_TX_S) && + dsaf_get_field(port_mode, XGMAC_PORT_MODE_RX_M, + XGMAC_PORT_MODE_RX_S); + mac_info->duplex = 1; + mac_info->speed = MAC_SPEED_10000; + + pause_ctrl = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + mac_info->rx_pause_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_RX_B); + mac_info->tx_pause_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_TX_B); +} + +/** + *hns_xgmac_get_pausefrm_cfg - get xgmac pause param + *@mac_drv: mac driver + *@rx_en:xgmac rx pause enable + *@tx_en:xgmac tx pause enable + */ +static void hns_xgmac_get_pausefrm_cfg(void *mac_drv, u32 *rx_en, u32 *tx_en) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 pause_ctrl; + + pause_ctrl = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + *rx_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_RX_B); + *tx_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_TX_B); +} + +/** + *hns_xgmac_get_link_status - get xgmac link status + *@mac_drv: mac driver + *@link_stat: xgmac link stat + */ +static void hns_xgmac_get_link_status(void *mac_drv, u32 *link_stat) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *link_stat = dsaf_read_dev(drv, XGMAC_LINK_STATUS_REG); +} + +/** + *hns_xgmac_get_regs - dump xgmac regs + *@mac_drv: mac driver + *@cmd:ethtool cmd + *@data:data for value of regs + */ +static void hns_xgmac_get_regs(void *mac_drv, void *data) +{ + u32 i = 0; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 *regs = data; + u64 qtmp; + + /* base config registers */ + regs[0] = dsaf_read_dev(drv, XGMAC_INT_STATUS_REG); + regs[1] = dsaf_read_dev(drv, XGMAC_INT_ENABLE_REG); + regs[2] = dsaf_read_dev(drv, XGMAC_INT_SET_REG); + regs[3] = dsaf_read_dev(drv, XGMAC_IERR_U_INFO_REG); + regs[4] = dsaf_read_dev(drv, XGMAC_OVF_INFO_REG); + regs[5] = dsaf_read_dev(drv, XGMAC_OVF_CNT_REG); + regs[6] = dsaf_read_dev(drv, XGMAC_PORT_MODE_REG); + regs[7] = dsaf_read_dev(drv, XGMAC_CLK_ENABLE_REG); + regs[8] = dsaf_read_dev(drv, XGMAC_RESET_REG); + regs[9] = dsaf_read_dev(drv, XGMAC_LINK_CONTROL_REG); + regs[10] = dsaf_read_dev(drv, XGMAC_LINK_STATUS_REG); + + regs[11] = dsaf_read_dev(drv, XGMAC_SPARE_REG); + regs[12] = dsaf_read_dev(drv, XGMAC_SPARE_CNT_REG); + regs[13] = dsaf_read_dev(drv, XGMAC_MAC_ENABLE_REG); + regs[14] = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG); + regs[15] = dsaf_read_dev(drv, XGMAC_MAC_IPG_REG); + regs[16] = dsaf_read_dev(drv, XGMAC_MAC_MSG_CRC_EN_REG); + regs[17] = dsaf_read_dev(drv, XGMAC_MAC_MSG_IMG_REG); + regs[18] = dsaf_read_dev(drv, XGMAC_MAC_MSG_FC_CFG_REG); + regs[19] = dsaf_read_dev(drv, XGMAC_MAC_MSG_TC_CFG_REG); + regs[20] = dsaf_read_dev(drv, XGMAC_MAC_PAD_SIZE_REG); + regs[21] = dsaf_read_dev(drv, XGMAC_MAC_MIN_PKT_SIZE_REG); + regs[22] = dsaf_read_dev(drv, XGMAC_MAC_MAX_PKT_SIZE_REG); + regs[23] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + regs[24] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_TIME_REG); + regs[25] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_GAP_REG); + regs[26] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG); + regs[27] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG); + regs[28] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_PEER_MAC_H_REG); + regs[29] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_PEER_MAC_L_REG); + regs[30] = dsaf_read_dev(drv, XGMAC_MAC_PFC_PRI_EN_REG); + regs[31] = dsaf_read_dev(drv, XGMAC_MAC_1588_CTRL_REG); + regs[32] = dsaf_read_dev(drv, XGMAC_MAC_1588_TX_PORT_DLY_REG); + regs[33] = dsaf_read_dev(drv, XGMAC_MAC_1588_RX_PORT_DLY_REG); + regs[34] = dsaf_read_dev(drv, XGMAC_MAC_1588_ASYM_DLY_REG); + regs[35] = dsaf_read_dev(drv, XGMAC_MAC_1588_ADJUST_CFG_REG); + + regs[36] = dsaf_read_dev(drv, XGMAC_MAC_Y1731_ETH_TYPE_REG); + regs[37] = dsaf_read_dev(drv, XGMAC_MAC_MIB_CONTROL_REG); + regs[38] = dsaf_read_dev(drv, XGMAC_MAC_WAN_RATE_ADJUST_REG); + regs[39] = dsaf_read_dev(drv, XGMAC_MAC_TX_ERR_MARK_REG); + regs[40] = dsaf_read_dev(drv, XGMAC_MAC_TX_LF_RF_CONTROL_REG); + regs[41] = dsaf_read_dev(drv, XGMAC_MAC_RX_LF_RF_STATUS_REG); + regs[42] = dsaf_read_dev(drv, XGMAC_MAC_TX_RUNT_PKT_CNT_REG); + regs[43] = dsaf_read_dev(drv, XGMAC_MAC_RX_RUNT_PKT_CNT_REG); + regs[44] = dsaf_read_dev(drv, XGMAC_MAC_RX_PREAM_ERR_PKT_CNT_REG); + regs[45] = dsaf_read_dev(drv, XGMAC_MAC_TX_LF_RF_TERM_PKT_CNT_REG); + regs[46] = dsaf_read_dev(drv, XGMAC_MAC_TX_SN_MISMATCH_PKT_CNT_REG); + regs[47] = dsaf_read_dev(drv, XGMAC_MAC_RX_ERR_MSG_CNT_REG); + regs[48] = dsaf_read_dev(drv, XGMAC_MAC_RX_ERR_EFD_CNT_REG); + regs[49] = dsaf_read_dev(drv, XGMAC_MAC_ERR_INFO_REG); + regs[50] = dsaf_read_dev(drv, XGMAC_MAC_DBG_INFO_REG); + + regs[51] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SYNC_THD_REG); + regs[52] = dsaf_read_dev(drv, XGMAC_PCS_STATUS1_REG); + regs[53] = dsaf_read_dev(drv, XGMAC_PCS_BASER_STATUS1_REG); + regs[54] = dsaf_read_dev(drv, XGMAC_PCS_BASER_STATUS2_REG); + regs[55] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDA_0_REG); + regs[56] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDA_1_REG); + regs[57] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDB_0_REG); + regs[58] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDB_1_REG); + regs[59] = dsaf_read_dev(drv, XGMAC_PCS_BASER_TEST_CONTROL_REG); + regs[60] = dsaf_read_dev(drv, XGMAC_PCS_BASER_TEST_ERR_CNT_REG); + regs[61] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO_REG); + regs[62] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO1_REG); + regs[63] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO2_REG); + regs[64] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO3_REG); + + regs[65] = dsaf_read_dev(drv, XGMAC_PMA_ENABLE_REG); + regs[66] = dsaf_read_dev(drv, XGMAC_PMA_CONTROL_REG); + regs[67] = dsaf_read_dev(drv, XGMAC_PMA_SIGNAL_STATUS_REG); + regs[68] = dsaf_read_dev(drv, XGMAC_PMA_DBG_INFO_REG); + regs[69] = dsaf_read_dev(drv, XGMAC_PMA_FEC_ABILITY_REG); + regs[70] = dsaf_read_dev(drv, XGMAC_PMA_FEC_CONTROL_REG); + regs[71] = dsaf_read_dev(drv, XGMAC_PMA_FEC_CORR_BLOCK_CNT__REG); + regs[72] = dsaf_read_dev(drv, XGMAC_PMA_FEC_UNCORR_BLOCK_CNT__REG); + + /* status registers */ +#define hns_xgmac_cpy_q(p, q) \ + do {\ + *(p) = (u32)(q);\ + *((p) + 1) = (u32)((q) >> 32);\ + } while (0) + + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_FRAGMENT); + hns_xgmac_cpy_q(®s[73], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERSIZE); + hns_xgmac_cpy_q(®s[75], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERMIN); + hns_xgmac_cpy_q(®s[77], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_64OCTETS); + hns_xgmac_cpy_q(®s[79], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_65TO127OCTETS); + hns_xgmac_cpy_q(®s[81], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_128TO255OCTETS); + hns_xgmac_cpy_q(®s[83], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_256TO511OCTETS); + hns_xgmac_cpy_q(®s[85], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_512TO1023OCTETS); + hns_xgmac_cpy_q(®s[87], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1024TO1518OCTETS); + hns_xgmac_cpy_q(®s[89], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETS); + hns_xgmac_cpy_q(®s[91], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETSOK); + hns_xgmac_cpy_q(®s[93], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_OVERSIZE); + hns_xgmac_cpy_q(®s[95], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_JABBER); + hns_xgmac_cpy_q(®s[97], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_GOODPKTS); + hns_xgmac_cpy_q(®s[99], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_GOODOCTETS); + hns_xgmac_cpy_q(®s[101], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_TOTAL_PKTS); + hns_xgmac_cpy_q(®s[103], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_TOTALOCTETS); + hns_xgmac_cpy_q(®s[105], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_UNICASTPKTS); + hns_xgmac_cpy_q(®s[107], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_MULTICASTPKTS); + hns_xgmac_cpy_q(®s[109], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_BROADCASTPKTS); + hns_xgmac_cpy_q(®s[111], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI0PAUSEPKTS); + hns_xgmac_cpy_q(®s[113], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI1PAUSEPKTS); + hns_xgmac_cpy_q(®s[115], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI2PAUSEPKTS); + hns_xgmac_cpy_q(®s[117], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI3PAUSEPKTS); + hns_xgmac_cpy_q(®s[119], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI4PAUSEPKTS); + hns_xgmac_cpy_q(®s[121], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI5PAUSEPKTS); + hns_xgmac_cpy_q(®s[123], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI6PAUSEPKTS); + hns_xgmac_cpy_q(®s[125], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI7PAUSEPKTS); + hns_xgmac_cpy_q(®s[127], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_MACCTRLPKTS); + hns_xgmac_cpy_q(®s[129], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_1731PKTS); + hns_xgmac_cpy_q(®s[131], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_1588PKTS); + hns_xgmac_cpy_q(®s[133], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPGOODPKTS); + hns_xgmac_cpy_q(®s[135], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPBADPKTS); + hns_xgmac_cpy_q(®s[137], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_ERRALLPKTS); + hns_xgmac_cpy_q(®s[139], qtmp); + + /* RX */ + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_FRAGMENT); + hns_xgmac_cpy_q(®s[141], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTSUNDERSIZE); + hns_xgmac_cpy_q(®s[143], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_UNDERMIN); + hns_xgmac_cpy_q(®s[145], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_64OCTETS); + hns_xgmac_cpy_q(®s[147], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_65TO127OCTETS); + hns_xgmac_cpy_q(®s[149], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_128TO255OCTETS); + hns_xgmac_cpy_q(®s[151], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_256TO511OCTETS); + hns_xgmac_cpy_q(®s[153], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_512TO1023OCTETS); + hns_xgmac_cpy_q(®s[155], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1024TO1518OCTETS); + hns_xgmac_cpy_q(®s[157], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETS); + hns_xgmac_cpy_q(®s[159], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETSOK); + hns_xgmac_cpy_q(®s[161], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_OVERSIZE); + hns_xgmac_cpy_q(®s[163], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_JABBER); + hns_xgmac_cpy_q(®s[165], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_GOODPKTS); + hns_xgmac_cpy_q(®s[167], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_GOODOCTETS); + hns_xgmac_cpy_q(®s[169], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_TOTAL_PKTS); + hns_xgmac_cpy_q(®s[171], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_TOTALOCTETS); + hns_xgmac_cpy_q(®s[173], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_UNICASTPKTS); + hns_xgmac_cpy_q(®s[175], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_MULTICASTPKTS); + hns_xgmac_cpy_q(®s[177], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_BROADCASTPKTS); + hns_xgmac_cpy_q(®s[179], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI0PAUSEPKTS); + hns_xgmac_cpy_q(®s[181], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI1PAUSEPKTS); + hns_xgmac_cpy_q(®s[183], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI2PAUSEPKTS); + hns_xgmac_cpy_q(®s[185], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI3PAUSEPKTS); + hns_xgmac_cpy_q(®s[187], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI4PAUSEPKTS); + hns_xgmac_cpy_q(®s[189], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI5PAUSEPKTS); + hns_xgmac_cpy_q(®s[191], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI6PAUSEPKTS); + hns_xgmac_cpy_q(®s[193], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI7PAUSEPKTS); + hns_xgmac_cpy_q(®s[195], qtmp); + + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_MACCTRLPKTS); + hns_xgmac_cpy_q(®s[197], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPGOODPKTS); + hns_xgmac_cpy_q(®s[199], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPBADPKTS); + hns_xgmac_cpy_q(®s[201], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_1731PKTS); + hns_xgmac_cpy_q(®s[203], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_SYMBOLERRPKTS); + hns_xgmac_cpy_q(®s[205], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FCSERRPKTS); + hns_xgmac_cpy_q(®s[207], qtmp); + + /* mark end of mac regs */ + for (i = 208; i < 214; i++) + regs[i] = 0xaaaaaaaa; +} + +/** + *hns_xgmac_get_stats - get xgmac statistic + *@mac_drv: mac driver + *@data:data for value of stats regs + */ +static void hns_xgmac_get_stats(void *mac_drv, u64 *data) +{ + u32 i; + u64 *buf = data; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct mac_hw_stats *hw_stats = NULL; + + hw_stats = &drv->mac_cb->hw_stats; + + for (i = 0; i < ARRAY_SIZE(g_xgmac_stats_string); i++) { + buf[i] = DSAF_STATS_READ(hw_stats, + g_xgmac_stats_string[i].offset); + } +} + +/** + *hns_xgmac_get_strings - get xgmac strings name + *@stringset: type of values in data + *@data:data for value of string name + */ +static void hns_xgmac_get_strings(u32 stringset, u8 *data) +{ + char *buff = (char *)data; + u32 i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(g_xgmac_stats_string); i++) { + snprintf(buff, ETH_GSTRING_LEN, g_xgmac_stats_string[i].desc); + buff = buff + ETH_GSTRING_LEN; + } +} + +/** + *hns_xgmac_get_sset_count - get xgmac string set count + *@stringset: type of values in data + *return xgmac string set count + */ +static int hns_xgmac_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return ARRAY_SIZE(g_xgmac_stats_string); + + return 0; +} + +/** + *hns_xgmac_get_regs_count - get xgmac regs count + *return xgmac regs count + */ +static int hns_xgmac_get_regs_count(void) +{ + return ETH_XGMAC_DUMP_NUM; +} + +void *hns_xgmac_config(struct hns_mac_cb *mac_cb, struct mac_params *mac_param) +{ + struct mac_driver *mac_drv; + + mac_drv = devm_kzalloc(mac_cb->dev, sizeof(*mac_drv), GFP_KERNEL); + if (!mac_drv) + return NULL; + + mac_drv->mac_init = hns_xgmac_init; + mac_drv->mac_enable = hns_xgmac_enable; + mac_drv->mac_disable = hns_xgmac_disable; + + mac_drv->mac_id = mac_param->mac_id; + mac_drv->mac_mode = mac_param->mac_mode; + mac_drv->io_base = mac_param->vaddr; + mac_drv->dev = mac_param->dev; + mac_drv->mac_cb = mac_cb; + + mac_drv->set_mac_addr = hns_xgmac_set_pausefrm_mac_addr; + mac_drv->set_an_mode = NULL; + mac_drv->config_loopback = NULL; + mac_drv->config_pad_and_crc = hns_xgmac_config_pad_and_crc; + mac_drv->config_half_duplex = NULL; + mac_drv->set_rx_ignore_pause_frames = + hns_xgmac_set_rx_ignore_pause_frames; + mac_drv->mac_get_id = hns_xgmac_get_id; + mac_drv->mac_free = hns_xgmac_free; + mac_drv->adjust_link = NULL; + mac_drv->set_tx_auto_pause_frames = hns_xgmac_set_tx_auto_pause_frames; + mac_drv->config_max_frame_length = hns_xgmac_config_max_frame_length; + mac_drv->mac_pausefrm_cfg = hns_xgmac_pausefrm_cfg; + mac_drv->autoneg_stat = NULL; + mac_drv->get_info = hns_xgmac_get_info; + mac_drv->get_pause_enable = hns_xgmac_get_pausefrm_cfg; + mac_drv->get_link_status = hns_xgmac_get_link_status; + mac_drv->get_regs = hns_xgmac_get_regs; + mac_drv->get_ethtool_stats = hns_xgmac_get_stats; + mac_drv->get_sset_count = hns_xgmac_get_sset_count; + mac_drv->get_regs_count = hns_xgmac_get_regs_count; + mac_drv->get_strings = hns_xgmac_get_strings; + mac_drv->update_stats = hns_xgmac_update_stats; + + return (void *)mac_drv; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h new file mode 100644 index 000000000000..139f7297c7b4 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_XGMAC_H +#define _HNS_XGMAC_H + +#define ETH_XGMAC_DUMP_NUM (214) + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c new file mode 100644 index 000000000000..ce7f2e0e3fd1 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -0,0 +1,1629 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/cpumask.h> +#include <linux/etherdevice.h> +#include <linux/if_vlan.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/module.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <linux/skbuff.h> + +#include "hnae.h" +#include "hns_enet.h" + +#define NIC_MAX_Q_PER_VF 16 +#define HNS_NIC_TX_TIMEOUT (5 * HZ) + +#define SERVICE_TIMER_HZ (1 * HZ) + +#define NIC_TX_CLEAN_MAX_NUM 256 +#define NIC_RX_CLEAN_MAX_NUM 64 + +#define RCB_IRQ_NOT_INITED 0 +#define RCB_IRQ_INITED 1 + +static void fill_desc(struct hnae_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + int buf_num, enum hns_desc_type type) +{ + struct hnae_desc *desc = &ring->desc[ring->next_to_use]; + struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use]; + struct sk_buff *skb; + __be16 protocol; + u32 ip_offset; + u32 asid_bufnum_pid = 0; + u32 flag_ipoffset = 0; + + desc_cb->priv = priv; + desc_cb->length = size; + desc_cb->dma = dma; + desc_cb->type = type; + + desc->addr = cpu_to_le64(dma); + desc->tx.send_size = cpu_to_le16((u16)size); + + /*config bd buffer end */ + flag_ipoffset |= 1 << HNS_TXD_VLD_B; + + asid_bufnum_pid |= buf_num << HNS_TXD_BUFNUM_S; + + if (type == DESC_TYPE_SKB) { + skb = (struct sk_buff *)priv; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + protocol = skb->protocol; + ip_offset = ETH_HLEN; + + /*if it is a SW VLAN check the next protocol*/ + if (protocol == htons(ETH_P_8021Q)) { + ip_offset += VLAN_HLEN; + protocol = vlan_get_protocol(skb); + skb->protocol = protocol; + } + + if (skb->protocol == htons(ETH_P_IP)) { + flag_ipoffset |= 1 << HNS_TXD_L3CS_B; + /* check for tcp/udp header */ + flag_ipoffset |= 1 << HNS_TXD_L4CS_B; + + } else if (skb->protocol == htons(ETH_P_IPV6)) { + /* ipv6 has not l3 cs, check for L4 header */ + flag_ipoffset |= 1 << HNS_TXD_L4CS_B; + } + + flag_ipoffset |= ip_offset << HNS_TXD_IPOFFSET_S; + } + } + + flag_ipoffset |= frag_end << HNS_TXD_FE_B; + + desc->tx.asid_bufnum_pid = cpu_to_le16(asid_bufnum_pid); + desc->tx.flag_ipoffset = cpu_to_le32(flag_ipoffset); + + ring_ptr_move_fw(ring, next_to_use); +} + +static void unfill_desc(struct hnae_ring *ring) +{ + ring_ptr_move_bw(ring, next_to_use); +} + +int hns_nic_net_xmit_hw(struct net_device *ndev, + struct sk_buff *skb, + struct hns_nic_ring_data *ring_data) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct device *dev = priv->dev; + struct hnae_ring *ring = ring_data->ring; + struct netdev_queue *dev_queue; + struct skb_frag_struct *frag; + int buf_num; + dma_addr_t dma; + int size, next_to_use; + int i, j; + struct sk_buff *new_skb; + + assert(ring->max_desc_num_per_pkt <= ring->desc_num); + + /* no. of segments (plus a header) */ + buf_num = skb_shinfo(skb)->nr_frags + 1; + + if (unlikely(buf_num > ring->max_desc_num_per_pkt)) { + if (ring_space(ring) < 1) { + ring->stats.tx_busy++; + goto out_net_tx_busy; + } + + new_skb = skb_copy(skb, GFP_ATOMIC); + if (!new_skb) { + ring->stats.sw_err_cnt++; + netdev_err(ndev, "no memory to xmit!\n"); + goto out_err_tx_ok; + } + + dev_kfree_skb_any(skb); + skb = new_skb; + buf_num = 1; + assert(skb_shinfo(skb)->nr_frags == 1); + } else if (buf_num > ring_space(ring)) { + ring->stats.tx_busy++; + goto out_net_tx_busy; + } + next_to_use = ring->next_to_use; + + /* fill the first part */ + size = skb_headlen(skb); + dma = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma)) { + netdev_err(ndev, "TX head DMA map failed\n"); + ring->stats.sw_err_cnt++; + goto out_err_tx_ok; + } + fill_desc(ring, skb, size, dma, buf_num == 1 ? 1 : 0, buf_num, + DESC_TYPE_SKB); + + /* fill the fragments */ + for (i = 1; i < buf_num; i++) { + frag = &skb_shinfo(skb)->frags[i - 1]; + size = skb_frag_size(frag); + dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma)) { + netdev_err(ndev, "TX frag(%d) DMA map failed\n", i); + ring->stats.sw_err_cnt++; + goto out_map_frag_fail; + } + fill_desc(ring, skb_frag_page(frag), size, dma, + buf_num - 1 == i ? 1 : 0, buf_num, DESC_TYPE_PAGE); + } + + /*complete translate all packets*/ + dev_queue = netdev_get_tx_queue(ndev, skb->queue_mapping); + netdev_tx_sent_queue(dev_queue, skb->len); + + wmb(); /* commit all data before submit */ + assert(skb->queue_mapping < priv->ae_handle->q_num); + hnae_queue_xmit(priv->ae_handle->qs[skb->queue_mapping], buf_num); + ring->stats.tx_pkts++; + ring->stats.tx_bytes += skb->len; + + return NETDEV_TX_OK; + +out_map_frag_fail: + + for (j = i - 1; j > 0; j--) { + unfill_desc(ring); + next_to_use = ring->next_to_use; + dma_unmap_page(dev, ring->desc_cb[next_to_use].dma, + ring->desc_cb[next_to_use].length, + DMA_TO_DEVICE); + } + + unfill_desc(ring); + next_to_use = ring->next_to_use; + dma_unmap_single(dev, ring->desc_cb[next_to_use].dma, + ring->desc_cb[next_to_use].length, DMA_TO_DEVICE); + +out_err_tx_ok: + + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + +out_net_tx_busy: + + netif_stop_subqueue(ndev, skb->queue_mapping); + + /* Herbert's original patch had: + * smp_mb__after_netif_stop_queue(); + * but since that doesn't exist yet, just open code it. + */ + smp_mb(); + return NETDEV_TX_BUSY; +} + +/** + * hns_nic_get_headlen - determine size of header for RSC/LRO/GRO/FCOE + * @data: pointer to the start of the headers + * @max: total length of section to find headers in + * + * This function is meant to determine the length of headers that will + * be recognized by hardware for LRO, GRO, and RSC offloads. The main + * motivation of doing this is to only perform one pull for IPv4 TCP + * packets so that we can do basic things like calculating the gso_size + * based on the average data per packet. + **/ +static unsigned int hns_nic_get_headlen(unsigned char *data, u32 flag, + unsigned int max_size) +{ + unsigned char *network; + u8 hlen; + + /* this should never happen, but better safe than sorry */ + if (max_size < ETH_HLEN) + return max_size; + + /* initialize network frame pointer */ + network = data; + + /* set first protocol and move network header forward */ + network += ETH_HLEN; + + /* handle any vlan tag if present */ + if (hnae_get_field(flag, HNS_RXD_VLAN_M, HNS_RXD_VLAN_S) + == HNS_RX_FLAG_VLAN_PRESENT) { + if ((typeof(max_size))(network - data) > (max_size - VLAN_HLEN)) + return max_size; + + network += VLAN_HLEN; + } + + /* handle L3 protocols */ + if (hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S) + == HNS_RX_FLAG_L3ID_IPV4) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct iphdr))) + return max_size; + + /* access ihl as a u8 to avoid unaligned access on ia64 */ + hlen = (network[0] & 0x0F) << 2; + + /* verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct iphdr)) + return network - data; + + /* record next protocol if header is present */ + } else if (hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S) + == HNS_RX_FLAG_L3ID_IPV6) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct ipv6hdr))) + return max_size; + + /* record next protocol */ + hlen = sizeof(struct ipv6hdr); + } else { + return network - data; + } + + /* relocate pointer to start of L4 header */ + network += hlen; + + /* finally sort out TCP/UDP */ + if (hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S) + == HNS_RX_FLAG_L4ID_TCP) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct tcphdr))) + return max_size; + + /* access doff as a u8 to avoid unaligned access on ia64 */ + hlen = (network[12] & 0xF0) >> 2; + + /* verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct tcphdr)) + return network - data; + + network += hlen; + } else if (hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S) + == HNS_RX_FLAG_L4ID_UDP) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct udphdr))) + return max_size; + + network += sizeof(struct udphdr); + } + + /* If everything has gone correctly network should be the + * data section of the packet and will be the end of the header. + * If not then it probably represents the end of the last recognized + * header. + */ + if ((typeof(max_size))(network - data) < max_size) + return network - data; + else + return max_size; +} + +static void +hns_nic_reuse_page(struct hnae_desc_cb *desc_cb, int tsize, int last_offset) +{ + /* avoid re-using remote pages,flag default unreuse */ + if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) { + /* move offset up to the next cache line */ + desc_cb->page_offset += tsize; + + if (desc_cb->page_offset <= last_offset) { + desc_cb->reuse_flag = 1; + /* bump ref count on page before it is given*/ + get_page(desc_cb->priv); + } + } +} + +static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, + struct sk_buff **out_skb, int *out_bnum) +{ + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + struct sk_buff *skb; + struct hnae_desc *desc; + struct hnae_desc_cb *desc_cb; + unsigned char *va; + int bnum, length, size, i, truesize, last_offset; + int pull_len; + u32 bnum_flag; + + last_offset = hnae_page_size(ring) - hnae_buf_size(ring); + desc = &ring->desc[ring->next_to_clean]; + desc_cb = &ring->desc_cb[ring->next_to_clean]; + length = le16_to_cpu(desc->rx.pkt_len); + bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag); + bnum = hnae_get_field(bnum_flag, HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S); + *out_bnum = bnum; + va = (unsigned char *)desc_cb->buf + desc_cb->page_offset; + + skb = *out_skb = napi_alloc_skb(&ring_data->napi, HNS_RX_HEAD_SIZE); + if (unlikely(!skb)) { + netdev_err(ndev, "alloc rx skb fail\n"); + ring->stats.sw_err_cnt++; + return -ENOMEM; + } + + if (length <= HNS_RX_HEAD_SIZE) { + memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long))); + + /* we can reuse buffer as-is, just make sure it is local */ + if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) + desc_cb->reuse_flag = 1; + else /* this page cannot be reused so discard it */ + put_page(desc_cb->priv); + + ring_ptr_move_fw(ring, next_to_clean); + + if (unlikely(bnum != 1)) { /* check err*/ + *out_bnum = 1; + goto out_bnum_err; + } + } else { + ring->stats.seg_pkt_cnt++; + + pull_len = hns_nic_get_headlen(va, bnum_flag, HNS_RX_HEAD_SIZE); + memcpy(__skb_put(skb, pull_len), va, + ALIGN(pull_len, sizeof(long))); + + size = le16_to_cpu(desc->rx.size); + truesize = ALIGN(size, L1_CACHE_BYTES); + skb_add_rx_frag(skb, 0, desc_cb->priv, + desc_cb->page_offset + pull_len, + size - pull_len, truesize - pull_len); + + hns_nic_reuse_page(desc_cb, truesize, last_offset); + ring_ptr_move_fw(ring, next_to_clean); + + if (unlikely(bnum >= (int)MAX_SKB_FRAGS)) { /* check err*/ + *out_bnum = 1; + goto out_bnum_err; + } + for (i = 1; i < bnum; i++) { + desc = &ring->desc[ring->next_to_clean]; + desc_cb = &ring->desc_cb[ring->next_to_clean]; + size = le16_to_cpu(desc->rx.size); + truesize = ALIGN(size, L1_CACHE_BYTES); + skb_add_rx_frag(skb, i, desc_cb->priv, + desc_cb->page_offset, + size, truesize); + + hns_nic_reuse_page(desc_cb, truesize, last_offset); + ring_ptr_move_fw(ring, next_to_clean); + } + } + + /* check except process, free skb and jump the desc */ + if (unlikely((!bnum) || (bnum > ring->max_desc_num_per_pkt))) { +out_bnum_err: + *out_bnum = *out_bnum ? *out_bnum : 1; /* ntc moved,cannot 0*/ + netdev_err(ndev, "invalid bnum(%d,%d,%d,%d),%016llx,%016llx\n", + bnum, ring->max_desc_num_per_pkt, + length, (int)MAX_SKB_FRAGS, + ((u64 *)desc)[0], ((u64 *)desc)[1]); + ring->stats.err_bd_num++; + dev_kfree_skb_any(skb); + return -EDOM; + } + + bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag); + + if (unlikely(!hnae_get_bit(bnum_flag, HNS_RXD_VLD_B))) { + netdev_err(ndev, "no valid bd,%016llx,%016llx\n", + ((u64 *)desc)[0], ((u64 *)desc)[1]); + ring->stats.non_vld_descs++; + dev_kfree_skb_any(skb); + return -EINVAL; + } + + if (unlikely((!desc->rx.pkt_len) || + hnae_get_bit(bnum_flag, HNS_RXD_DROP_B))) { + ring->stats.err_pkt_len++; + dev_kfree_skb_any(skb); + return -EFAULT; + } + + if (unlikely(hnae_get_bit(bnum_flag, HNS_RXD_L2E_B))) { + ring->stats.l2_err++; + dev_kfree_skb_any(skb); + return -EFAULT; + } + + ring->stats.rx_pkts++; + ring->stats.rx_bytes += skb->len; + + if (unlikely(hnae_get_bit(bnum_flag, HNS_RXD_L3E_B) || + hnae_get_bit(bnum_flag, HNS_RXD_L4E_B))) { + ring->stats.l3l4_csum_err++; + return 0; + } + + skb->ip_summed = CHECKSUM_UNNECESSARY; + + return 0; +} + +static void +hns_nic_alloc_rx_buffers(struct hns_nic_ring_data *ring_data, int cleand_count) +{ + int i, ret; + struct hnae_desc_cb res_cbs; + struct hnae_desc_cb *desc_cb; + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + + for (i = 0; i < cleand_count; i++) { + desc_cb = &ring->desc_cb[ring->next_to_use]; + if (desc_cb->reuse_flag) { + ring->stats.reuse_pg_cnt++; + hnae_reuse_buffer(ring, ring->next_to_use); + } else { + ret = hnae_reserve_buffer_map(ring, &res_cbs); + if (ret) { + ring->stats.sw_err_cnt++; + netdev_err(ndev, "hnae reserve buffer map failed.\n"); + break; + } + hnae_replace_buffer(ring, ring->next_to_use, &res_cbs); + } + + ring_ptr_move_fw(ring, next_to_use); + } + + wmb(); /* make all data has been write before submit */ + writel_relaxed(i, ring->io_base + RCB_REG_HEAD); +} + +/* return error number for error or number of desc left to take + */ +static void hns_nic_rx_up_pro(struct hns_nic_ring_data *ring_data, + struct sk_buff *skb) +{ + struct net_device *ndev = ring_data->napi.dev; + + skb->protocol = eth_type_trans(skb, ndev); + (void)napi_gro_receive(&ring_data->napi, skb); + ndev->last_rx = jiffies; +} + +static int hns_nic_rx_poll_one(struct hns_nic_ring_data *ring_data, + int budget, void *v) +{ + struct hnae_ring *ring = ring_data->ring; + struct sk_buff *skb; + int num, bnum, ex_num; +#define RCB_NOF_ALLOC_RX_BUFF_ONCE 16 + int recv_pkts, recv_bds, clean_count, err; + + num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); + rmb(); /* make sure num taken effect before the other data is touched */ + + recv_pkts = 0, recv_bds = 0, clean_count = 0; +recv: + while (recv_pkts < budget && recv_bds < num) { + /* reuse or realloc buffers*/ + if (clean_count >= RCB_NOF_ALLOC_RX_BUFF_ONCE) { + hns_nic_alloc_rx_buffers(ring_data, clean_count); + clean_count = 0; + } + + /* poll one pkg*/ + err = hns_nic_poll_rx_skb(ring_data, &skb, &bnum); + if (unlikely(!skb)) /* this fault cannot be repaired */ + break; + + recv_bds += bnum; + clean_count += bnum; + if (unlikely(err)) { /* do jump the err */ + recv_pkts++; + continue; + } + + /* do update ip stack process*/ + ((void (*)(struct hns_nic_ring_data *, struct sk_buff *))v)( + ring_data, skb); + recv_pkts++; + } + + /* make all data has been write before submit */ + if (clean_count > 0) { + hns_nic_alloc_rx_buffers(ring_data, clean_count); + clean_count = 0; + } + + if (recv_pkts < budget) { + ex_num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); + rmb(); /*complete read rx ring bd number*/ + if (ex_num > 0) { + num += ex_num; + goto recv; + } + } + + return recv_pkts; +} + +static void hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data) +{ + struct hnae_ring *ring = ring_data->ring; + int num = 0; + + /* for hardware bug fixed */ + num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); + + if (num > 0) { + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 1); + + napi_schedule(&ring_data->napi); + } +} + +static inline void hns_nic_reclaim_one_desc(struct hnae_ring *ring, + int *bytes, int *pkts) +{ + struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_clean]; + + (*pkts) += (desc_cb->type == DESC_TYPE_SKB); + (*bytes) += desc_cb->length; + /* desc_cb will be cleaned, after hnae_free_buffer_detach*/ + hnae_free_buffer_detach(ring, ring->next_to_clean); + + ring_ptr_move_fw(ring, next_to_clean); +} + +static int is_valid_clean_head(struct hnae_ring *ring, int h) +{ + int u = ring->next_to_use; + int c = ring->next_to_clean; + + if (unlikely(h > ring->desc_num)) + return 0; + + assert(u > 0 && u < ring->desc_num); + assert(c > 0 && c < ring->desc_num); + assert(u != c && h != c); /* must be checked before call this func */ + + return u > c ? (h > c && h <= u) : (h > c || h <= u); +} + +/* netif_tx_lock will turn down the performance, set only when necessary */ +#ifdef CONFIG_NET_POLL_CONTROLLER +#define NETIF_TX_LOCK(ndev) netif_tx_lock(ndev) +#define NETIF_TX_UNLOCK(ndev) netif_tx_unlock(ndev) +#else +#define NETIF_TX_LOCK(ndev) +#define NETIF_TX_UNLOCK(ndev) +#endif +/* reclaim all desc in one budget + * return error or number of desc left + */ +static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data, + int budget, void *v) +{ + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + struct netdev_queue *dev_queue; + struct hns_nic_priv *priv = netdev_priv(ndev); + int head; + int bytes, pkts; + + NETIF_TX_LOCK(ndev); + + head = readl_relaxed(ring->io_base + RCB_REG_HEAD); + rmb(); /* make sure head is ready before touch any data */ + + if (is_ring_empty(ring) || head == ring->next_to_clean) { + NETIF_TX_UNLOCK(ndev); + return 0; /* no data to poll */ + } + + if (!is_valid_clean_head(ring, head)) { + netdev_err(ndev, "wrong head (%d, %d-%d)\n", head, + ring->next_to_use, ring->next_to_clean); + ring->stats.io_err_cnt++; + NETIF_TX_UNLOCK(ndev); + return -EIO; + } + + bytes = 0; + pkts = 0; + while (head != ring->next_to_clean) + hns_nic_reclaim_one_desc(ring, &bytes, &pkts); + + NETIF_TX_UNLOCK(ndev); + + dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); + netdev_tx_completed_queue(dev_queue, pkts, bytes); + + if (unlikely(pkts && netif_carrier_ok(ndev) && + (ring_space(ring) >= ring->max_desc_num_per_pkt * 2))) { + /* Make sure that anybody stopping the queue after this + * sees the new next_to_clean. + */ + smp_mb(); + if (netif_tx_queue_stopped(dev_queue) && + !test_bit(NIC_STATE_DOWN, &priv->state)) { + netif_tx_wake_queue(dev_queue); + ring->stats.restart_queue++; + } + } + return 0; +} + +static void hns_nic_tx_fini_pro(struct hns_nic_ring_data *ring_data) +{ + struct hnae_ring *ring = ring_data->ring; + int head = ring->next_to_clean; + + /* for hardware bug fixed */ + head = readl_relaxed(ring->io_base + RCB_REG_HEAD); + + if (head != ring->next_to_clean) { + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 1); + + napi_schedule(&ring_data->napi); + } +} + +static void hns_nic_tx_clr_all_bufs(struct hns_nic_ring_data *ring_data) +{ + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + struct netdev_queue *dev_queue; + int head; + int bytes, pkts; + + NETIF_TX_LOCK(ndev); + + head = ring->next_to_use; /* ntu :soft setted ring position*/ + bytes = 0; + pkts = 0; + while (head != ring->next_to_clean) + hns_nic_reclaim_one_desc(ring, &bytes, &pkts); + + NETIF_TX_UNLOCK(ndev); + + dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); + netdev_tx_reset_queue(dev_queue); +} + +static int hns_nic_common_poll(struct napi_struct *napi, int budget) +{ + struct hns_nic_ring_data *ring_data = + container_of(napi, struct hns_nic_ring_data, napi); + int clean_complete = ring_data->poll_one( + ring_data, budget, ring_data->ex_process); + + if (clean_complete >= 0 && clean_complete < budget) { + napi_complete(napi); + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 0); + + ring_data->fini_process(ring_data); + } + + return clean_complete; +} + +static irqreturn_t hns_irq_handle(int irq, void *dev) +{ + struct hns_nic_ring_data *ring_data = (struct hns_nic_ring_data *)dev; + + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 1); + napi_schedule(&ring_data->napi); + + return IRQ_HANDLED; +} + +/** + *hns_nic_adjust_link - adjust net work mode by the phy stat or new param + *@ndev: net device + */ +static void hns_nic_adjust_link(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + + h->dev->ops->adjust_link(h, ndev->phydev->speed, ndev->phydev->duplex); +} + +/** + *hns_nic_init_phy - init phy + *@ndev: net device + *@h: ae handle + * Return 0 on success, negative on failure + */ +int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct phy_device *phy_dev = NULL; + + if (!h->phy_node) + return 0; + + if (h->phy_if != PHY_INTERFACE_MODE_XGMII) + phy_dev = of_phy_connect(ndev, h->phy_node, + hns_nic_adjust_link, 0, h->phy_if); + else + phy_dev = of_phy_attach(ndev, h->phy_node, 0, h->phy_if); + + if (unlikely(!phy_dev) || IS_ERR(phy_dev)) + return !phy_dev ? -ENODEV : PTR_ERR(phy_dev); + + phy_dev->supported &= h->if_support; + phy_dev->advertising = phy_dev->supported; + + if (h->phy_if == PHY_INTERFACE_MODE_XGMII) + phy_dev->autoneg = false; + + priv->phy = phy_dev; + + return 0; +} + +static int hns_nic_ring_open(struct net_device *netdev, int idx) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + napi_enable(&priv->ring_data[idx].napi); + + enable_irq(priv->ring_data[idx].ring->irq); + h->dev->ops->toggle_ring_irq(priv->ring_data[idx].ring, 0); + + return 0; +} + +static int hns_nic_net_set_mac_address(struct net_device *ndev, void *p) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + struct sockaddr *mac_addr = p; + int ret; + + if (!mac_addr || !is_valid_ether_addr((const u8 *)mac_addr->sa_data)) + return -EADDRNOTAVAIL; + + ret = h->dev->ops->set_mac_addr(h, mac_addr->sa_data); + if (ret) { + netdev_err(ndev, "set_mac_address fail, ret=%d!\n", ret); + return ret; + } + + memcpy(ndev->dev_addr, mac_addr->sa_data, ndev->addr_len); + + return 0; +} + +void hns_nic_update_stats(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + h->dev->ops->update_stats(h, &netdev->stats); +} + +/* set mac addr if it is configed. or leave it to the AE driver */ +static void hns_init_mac_addr(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct device_node *node = priv->dev->of_node; + const void *mac_addr_temp; + + mac_addr_temp = of_get_mac_address(node); + if (mac_addr_temp && is_valid_ether_addr(mac_addr_temp)) { + memcpy(ndev->dev_addr, mac_addr_temp, ndev->addr_len); + } else { + eth_hw_addr_random(ndev); + dev_warn(priv->dev, "No valid mac, use random mac %pM", + ndev->dev_addr); + } +} + +static void hns_nic_ring_close(struct net_device *netdev, int idx) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + h->dev->ops->toggle_ring_irq(priv->ring_data[idx].ring, 1); + disable_irq(priv->ring_data[idx].ring->irq); + + napi_disable(&priv->ring_data[idx].napi); +} + +static int hns_nic_init_irq(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + struct hns_nic_ring_data *rd; + int i; + int ret; + int cpu; + cpumask_t mask; + + for (i = 0; i < h->q_num * 2; i++) { + rd = &priv->ring_data[i]; + + if (rd->ring->irq_init_flag == RCB_IRQ_INITED) + break; + + snprintf(rd->ring->ring_name, RCB_RING_NAME_LEN, + "%s-%s%d", priv->netdev->name, + (i < h->q_num ? "tx" : "rx"), rd->queue_index); + + rd->ring->ring_name[RCB_RING_NAME_LEN - 1] = '\0'; + + ret = request_irq(rd->ring->irq, + hns_irq_handle, 0, rd->ring->ring_name, rd); + if (ret) { + netdev_err(priv->netdev, "request irq(%d) fail\n", + rd->ring->irq); + return ret; + } + disable_irq(rd->ring->irq); + rd->ring->irq_init_flag = RCB_IRQ_INITED; + + /*set cpu affinity*/ + if (cpu_online(rd->queue_index)) { + cpumask_clear(&mask); + cpu = rd->queue_index; + cpumask_set_cpu(cpu, &mask); + irq_set_affinity_hint(rd->ring->irq, &mask); + } + } + + return 0; +} + +static int hns_nic_net_up(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int i, j, k; + int ret; + + ret = hns_nic_init_irq(priv); + if (ret != 0) { + netdev_err(ndev, "hns init irq failed! ret=%d\n", ret); + return ret; + } + + for (i = 0; i < h->q_num * 2; i++) { + ret = hns_nic_ring_open(ndev, i); + if (ret) + goto out_has_some_queues; + } + + for (k = 0; k < h->q_num; k++) + h->dev->ops->toggle_queue_status(h->qs[k], 1); + + ret = h->dev->ops->set_mac_addr(h, ndev->dev_addr); + if (ret) + goto out_set_mac_addr_err; + + ret = h->dev->ops->start ? h->dev->ops->start(h) : 0; + if (ret) + goto out_start_err; + + if (priv->phy) + phy_start(priv->phy); + + clear_bit(NIC_STATE_DOWN, &priv->state); + (void)mod_timer(&priv->service_timer, jiffies + SERVICE_TIMER_HZ); + + return 0; + +out_start_err: + netif_stop_queue(ndev); +out_set_mac_addr_err: + for (k = 0; k < h->q_num; k++) + h->dev->ops->toggle_queue_status(h->qs[k], 0); +out_has_some_queues: + for (j = i - 1; j >= 0; j--) + hns_nic_ring_close(ndev, j); + + set_bit(NIC_STATE_DOWN, &priv->state); + + return ret; +} + +static void hns_nic_net_down(struct net_device *ndev) +{ + int i; + struct hnae_ae_ops *ops; + struct hns_nic_priv *priv = netdev_priv(ndev); + + if (test_and_set_bit(NIC_STATE_DOWN, &priv->state)) + return; + + (void)del_timer_sync(&priv->service_timer); + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); + netif_tx_disable(ndev); + priv->link = 0; + + if (priv->phy) + phy_stop(priv->phy); + + ops = priv->ae_handle->dev->ops; + + if (ops->stop) + ops->stop(priv->ae_handle); + + netif_tx_stop_all_queues(ndev); + + for (i = priv->ae_handle->q_num - 1; i >= 0; i--) { + hns_nic_ring_close(ndev, i); + hns_nic_ring_close(ndev, i + priv->ae_handle->q_num); + + /* clean tx buffers*/ + hns_nic_tx_clr_all_bufs(priv->ring_data + i); + } +} + +void hns_nic_net_reset(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *handle = priv->ae_handle; + + while (test_and_set_bit(NIC_STATE_RESETTING, &priv->state)) + usleep_range(1000, 2000); + + (void)hnae_reinit_handle(handle); + + clear_bit(NIC_STATE_RESETTING, &priv->state); +} + +void hns_nic_net_reinit(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + + priv->netdev->trans_start = jiffies; + while (test_and_set_bit(NIC_STATE_REINITING, &priv->state)) + usleep_range(1000, 2000); + + hns_nic_net_down(netdev); + hns_nic_net_reset(netdev); + (void)hns_nic_net_up(netdev); + clear_bit(NIC_STATE_REINITING, &priv->state); +} + +static int hns_nic_net_open(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int ret; + + if (test_bit(NIC_STATE_TESTING, &priv->state)) + return -EBUSY; + + priv->link = 0; + netif_carrier_off(ndev); + + ret = netif_set_real_num_tx_queues(ndev, h->q_num); + if (ret < 0) { + netdev_err(ndev, "netif_set_real_num_tx_queues fail, ret=%d!\n", + ret); + return ret; + } + + ret = netif_set_real_num_rx_queues(ndev, h->q_num); + if (ret < 0) { + netdev_err(ndev, + "netif_set_real_num_rx_queues fail, ret=%d!\n", ret); + return ret; + } + + ret = hns_nic_net_up(ndev); + if (ret) { + netdev_err(ndev, + "hns net up fail, ret=%d!\n", ret); + return ret; + } + + return 0; +} + +static int hns_nic_net_stop(struct net_device *ndev) +{ + hns_nic_net_down(ndev); + + return 0; +} + +static void hns_tx_timeout_reset(struct hns_nic_priv *priv); +static void hns_nic_net_timeout(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + + hns_tx_timeout_reset(priv); +} + +static int hns_nic_do_ioctl(struct net_device *netdev, struct ifreq *ifr, + int cmd) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct phy_device *phy_dev = priv->phy; + + if (!netif_running(netdev)) + return -EINVAL; + + if (!phy_dev) + return -ENOTSUPP; + + return phy_mii_ioctl(phy_dev, ifr, cmd); +} + +/* use only for netconsole to poll with the device without interrupt */ +#ifdef CONFIG_NET_POLL_CONTROLLER +void hns_nic_poll_controller(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + unsigned long flags; + int i; + + local_irq_save(flags); + for (i = 0; i < priv->ae_handle->q_num * 2; i++) + napi_schedule(&priv->ring_data[i].napi); + local_irq_restore(flags); +} +#endif + +static netdev_tx_t hns_nic_net_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + int ret; + + assert(skb->queue_mapping < ndev->ae_handle->q_num); + ret = hns_nic_net_xmit_hw(ndev, skb, + &tx_ring_data(priv, skb->queue_mapping)); + if (ret == NETDEV_TX_OK) { + ndev->trans_start = jiffies; + ndev->stats.tx_bytes += skb->len; + ndev->stats.tx_packets++; + } + return (netdev_tx_t)ret; +} + +static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int ret; + + /* MTU < 68 is an error and causes problems on some kernels */ + if (new_mtu < 68) + return -EINVAL; + + if (!h->dev->ops->set_mtu) + return -ENOTSUPP; + + if (netif_running(ndev)) { + (void)hns_nic_net_stop(ndev); + msleep(100); + + ret = h->dev->ops->set_mtu(h, new_mtu); + if (ret) + netdev_err(ndev, "set mtu fail, return value %d\n", + ret); + + if (hns_nic_net_open(ndev)) + netdev_err(ndev, "hns net open fail\n"); + } else { + ret = h->dev->ops->set_mtu(h, new_mtu); + } + + if (!ret) + ndev->mtu = new_mtu; + + return ret; +} + +/** + * nic_set_multicast_list - set mutl mac address + * @netdev: net device + * @p: mac address + * + * return void + */ +void hns_set_multicast_list(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + struct netdev_hw_addr *ha = NULL; + + if (!h) { + netdev_err(ndev, "hnae handle is null\n"); + return; + } + + if (h->dev->ops->set_mc_addr) { + netdev_for_each_mc_addr(ha, ndev) + if (h->dev->ops->set_mc_addr(h, ha->addr)) + netdev_err(ndev, "set multicast fail\n"); + } +} + +struct rtnl_link_stats64 *hns_nic_get_stats64(struct net_device *ndev, + struct rtnl_link_stats64 *stats) +{ + int idx = 0; + u64 tx_bytes = 0; + u64 rx_bytes = 0; + u64 tx_pkts = 0; + u64 rx_pkts = 0; + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + + for (idx = 0; idx < h->q_num; idx++) { + tx_bytes += h->qs[idx]->tx_ring.stats.tx_bytes; + tx_pkts += h->qs[idx]->tx_ring.stats.tx_pkts; + rx_bytes += h->qs[idx]->rx_ring.stats.rx_bytes; + rx_pkts += h->qs[idx]->rx_ring.stats.rx_pkts; + } + + stats->tx_bytes = tx_bytes; + stats->tx_packets = tx_pkts; + stats->rx_bytes = rx_bytes; + stats->rx_packets = rx_pkts; + + stats->rx_errors = ndev->stats.rx_errors; + stats->multicast = ndev->stats.multicast; + stats->rx_length_errors = ndev->stats.rx_length_errors; + stats->rx_crc_errors = ndev->stats.rx_crc_errors; + stats->rx_missed_errors = ndev->stats.rx_missed_errors; + + stats->tx_errors = ndev->stats.tx_errors; + stats->rx_dropped = ndev->stats.rx_dropped; + stats->tx_dropped = ndev->stats.tx_dropped; + stats->collisions = ndev->stats.collisions; + stats->rx_over_errors = ndev->stats.rx_over_errors; + stats->rx_frame_errors = ndev->stats.rx_frame_errors; + stats->rx_fifo_errors = ndev->stats.rx_fifo_errors; + stats->tx_aborted_errors = ndev->stats.tx_aborted_errors; + stats->tx_carrier_errors = ndev->stats.tx_carrier_errors; + stats->tx_fifo_errors = ndev->stats.tx_fifo_errors; + stats->tx_heartbeat_errors = ndev->stats.tx_heartbeat_errors; + stats->tx_window_errors = ndev->stats.tx_window_errors; + stats->rx_compressed = ndev->stats.rx_compressed; + stats->tx_compressed = ndev->stats.tx_compressed; + + return stats; +} + +static const struct net_device_ops hns_nic_netdev_ops = { + .ndo_open = hns_nic_net_open, + .ndo_stop = hns_nic_net_stop, + .ndo_start_xmit = hns_nic_net_xmit, + .ndo_tx_timeout = hns_nic_net_timeout, + .ndo_set_mac_address = hns_nic_net_set_mac_address, + .ndo_change_mtu = hns_nic_change_mtu, + .ndo_do_ioctl = hns_nic_do_ioctl, + .ndo_get_stats64 = hns_nic_get_stats64, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = hns_nic_poll_controller, +#endif + .ndo_set_rx_mode = hns_set_multicast_list, +}; + +static void hns_nic_update_link_status(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + + struct hnae_handle *h = priv->ae_handle; + int state = 1; + + if (priv->phy) { + if (!genphy_update_link(priv->phy)) + state = priv->phy->link; + else + state = 0; + } + state = state && h->dev->ops->get_status(h); + + if (state != priv->link) { + if (state) { + netif_carrier_on(netdev); + netif_tx_wake_all_queues(netdev); + netdev_info(netdev, "link up\n"); + } else { + netif_carrier_off(netdev); + netdev_info(netdev, "link down\n"); + } + priv->link = state; + } +} + +/* for dumping key regs*/ +static void hns_nic_dump(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + struct hnae_ae_ops *ops = h->dev->ops; + u32 *data, reg_num, i; + + if (ops->get_regs_len && ops->get_regs) { + reg_num = ops->get_regs_len(priv->ae_handle); + reg_num = (reg_num + 3ul) & ~3ul; + data = kcalloc(reg_num, sizeof(u32), GFP_KERNEL); + if (data) { + ops->get_regs(priv->ae_handle, data); + for (i = 0; i < reg_num; i += 4) + pr_info("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n", + i, data[i], data[i + 1], + data[i + 2], data[i + 3]); + kfree(data); + } + } + + for (i = 0; i < h->q_num; i++) { + pr_info("tx_queue%d_next_to_clean:%d\n", + i, h->qs[i]->tx_ring.next_to_clean); + pr_info("tx_queue%d_next_to_use:%d\n", + i, h->qs[i]->tx_ring.next_to_use); + pr_info("rx_queue%d_next_to_clean:%d\n", + i, h->qs[i]->rx_ring.next_to_clean); + pr_info("rx_queue%d_next_to_use:%d\n", + i, h->qs[i]->rx_ring.next_to_use); + } +} + +/* for resetting suntask*/ +static void hns_nic_reset_subtask(struct hns_nic_priv *priv) +{ + enum hnae_port_type type = priv->ae_handle->port_type; + + if (!test_bit(NIC_STATE2_RESET_REQUESTED, &priv->state)) + return; + clear_bit(NIC_STATE2_RESET_REQUESTED, &priv->state); + + /* If we're already down, removing or resetting, just bail */ + if (test_bit(NIC_STATE_DOWN, &priv->state) || + test_bit(NIC_STATE_REMOVING, &priv->state) || + test_bit(NIC_STATE_RESETTING, &priv->state)) + return; + + hns_nic_dump(priv); + netdev_err(priv->netdev, "Reset %s port\n", + (type == HNAE_PORT_DEBUG ? "debug" : "business")); + + rtnl_lock(); + if (type == HNAE_PORT_DEBUG) { + hns_nic_net_reinit(priv->netdev); + } else { + hns_nic_net_down(priv->netdev); + hns_nic_net_reset(priv->netdev); + } + rtnl_unlock(); +} + +/* for doing service complete*/ +static void hns_nic_service_event_complete(struct hns_nic_priv *priv) +{ + assert(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state)); + + smp_mb__before_atomic(); + clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state); +} + +static void hns_nic_service_task(struct work_struct *work) +{ + struct hns_nic_priv *priv + = container_of(work, struct hns_nic_priv, service_task); + struct hnae_handle *h = priv->ae_handle; + + hns_nic_update_link_status(priv->netdev); + h->dev->ops->update_led_status(h); + hns_nic_update_stats(priv->netdev); + + hns_nic_reset_subtask(priv); + hns_nic_service_event_complete(priv); +} + +static void hns_nic_task_schedule(struct hns_nic_priv *priv) +{ + if (!test_bit(NIC_STATE_DOWN, &priv->state) && + !test_bit(NIC_STATE_REMOVING, &priv->state) && + !test_and_set_bit(NIC_STATE_SERVICE_SCHED, &priv->state)) + (void)schedule_work(&priv->service_task); +} + +static void hns_nic_service_timer(unsigned long data) +{ + struct hns_nic_priv *priv = (struct hns_nic_priv *)data; + + (void)mod_timer(&priv->service_timer, jiffies + SERVICE_TIMER_HZ); + + hns_nic_task_schedule(priv); +} + +/** + * hns_tx_timeout_reset - initiate reset due to Tx timeout + * @priv: driver private struct + **/ +static void hns_tx_timeout_reset(struct hns_nic_priv *priv) +{ + /* Do the reset outside of interrupt context */ + if (!test_bit(NIC_STATE_DOWN, &priv->state)) { + set_bit(NIC_STATE2_RESET_REQUESTED, &priv->state); + netdev_warn(priv->netdev, + "initiating reset due to tx timeout(%llu,0x%lx)\n", + priv->tx_timeout_count, priv->state); + priv->tx_timeout_count++; + hns_nic_task_schedule(priv); + } +} + +static int hns_nic_init_ring_data(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + struct hns_nic_ring_data *rd; + int i; + + if (h->q_num > NIC_MAX_Q_PER_VF) { + netdev_err(priv->netdev, "too much queue (%d)\n", h->q_num); + return -EINVAL; + } + + priv->ring_data = kzalloc(h->q_num * sizeof(*priv->ring_data) * 2, + GFP_KERNEL); + if (!priv->ring_data) + return -ENOMEM; + + for (i = 0; i < h->q_num; i++) { + rd = &priv->ring_data[i]; + rd->queue_index = i; + rd->ring = &h->qs[i]->tx_ring; + rd->poll_one = hns_nic_tx_poll_one; + rd->fini_process = hns_nic_tx_fini_pro; + + netif_napi_add(priv->netdev, &rd->napi, + hns_nic_common_poll, NIC_TX_CLEAN_MAX_NUM); + rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED; + } + for (i = h->q_num; i < h->q_num * 2; i++) { + rd = &priv->ring_data[i]; + rd->queue_index = i - h->q_num; + rd->ring = &h->qs[i - h->q_num]->rx_ring; + rd->poll_one = hns_nic_rx_poll_one; + rd->ex_process = hns_nic_rx_up_pro; + rd->fini_process = hns_nic_rx_fini_pro; + + netif_napi_add(priv->netdev, &rd->napi, + hns_nic_common_poll, NIC_RX_CLEAN_MAX_NUM); + rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED; + } + + return 0; +} + +static void hns_nic_uninit_ring_data(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + int i; + + for (i = 0; i < h->q_num * 2; i++) { + netif_napi_del(&priv->ring_data[i].napi); + if (priv->ring_data[i].ring->irq_init_flag == RCB_IRQ_INITED) { + irq_set_affinity_hint(priv->ring_data[i].ring->irq, + NULL); + free_irq(priv->ring_data[i].ring->irq, + &priv->ring_data[i]); + } + + priv->ring_data[i].ring->irq_init_flag = RCB_IRQ_NOT_INITED; + } + kfree(priv->ring_data); +} + +static int hns_nic_try_get_ae(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h; + int ret; + + h = hnae_get_handle(&priv->netdev->dev, + priv->ae_name, priv->port_id, NULL); + if (IS_ERR_OR_NULL(h)) { + ret = PTR_ERR(h); + dev_dbg(priv->dev, "has not handle, register notifier!\n"); + goto out; + } + priv->ae_handle = h; + + ret = hns_nic_init_phy(ndev, h); + if (ret) { + dev_err(priv->dev, "probe phy device fail!\n"); + goto out_init_phy; + } + + ret = hns_nic_init_ring_data(priv); + if (ret) { + ret = -ENOMEM; + goto out_init_ring_data; + } + + ret = register_netdev(ndev); + if (ret) { + dev_err(priv->dev, "probe register netdev fail!\n"); + goto out_reg_ndev_fail; + } + return 0; + +out_reg_ndev_fail: + hns_nic_uninit_ring_data(priv); + priv->ring_data = NULL; +out_init_phy: +out_init_ring_data: + hnae_put_handle(priv->ae_handle); + priv->ae_handle = NULL; +out: + return ret; +} + +static int hns_nic_notifier_action(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct hns_nic_priv *priv = + container_of(nb, struct hns_nic_priv, notifier_block); + + assert(action == HNAE_AE_REGISTER); + + if (!hns_nic_try_get_ae(priv->netdev)) { + hnae_unregister_notifier(&priv->notifier_block); + priv->notifier_block.notifier_call = NULL; + } + return 0; +} + +static int hns_nic_dev_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct net_device *ndev; + struct hns_nic_priv *priv; + struct device_node *node = dev->of_node; + int ret; + + ndev = alloc_etherdev_mq(sizeof(struct hns_nic_priv), NIC_MAX_Q_PER_VF); + if (!ndev) + return -ENOMEM; + + platform_set_drvdata(pdev, ndev); + + priv = netdev_priv(ndev); + priv->dev = dev; + priv->netdev = ndev; + + if (of_device_is_compatible(node, "hisilicon,hns-nic-v2")) + priv->enet_ver = AE_VERSION_2; + else + priv->enet_ver = AE_VERSION_1; + + ret = of_property_read_string(node, "ae-name", &priv->ae_name); + if (ret) + goto out_read_string_fail; + + ret = of_property_read_u32(node, "port-id", &priv->port_id); + if (ret) + goto out_read_string_fail; + + hns_init_mac_addr(ndev); + + ndev->watchdog_timeo = HNS_NIC_TX_TIMEOUT; + ndev->priv_flags |= IFF_UNICAST_FLT; + ndev->netdev_ops = &hns_nic_netdev_ops; + hns_ethtool_set_ops(ndev); + ndev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | + NETIF_F_GRO; + ndev->vlan_features |= + NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM; + ndev->vlan_features |= NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO; + + SET_NETDEV_DEV(ndev, dev); + + if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) + dev_dbg(dev, "set mask to 64bit\n"); + else + dev_err(dev, "set mask to 32bit fail!\n"); + + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(ndev); + + setup_timer(&priv->service_timer, hns_nic_service_timer, + (unsigned long)priv); + INIT_WORK(&priv->service_task, hns_nic_service_task); + + set_bit(NIC_STATE_SERVICE_INITED, &priv->state); + clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state); + set_bit(NIC_STATE_DOWN, &priv->state); + + if (hns_nic_try_get_ae(priv->netdev)) { + priv->notifier_block.notifier_call = hns_nic_notifier_action; + ret = hnae_register_notifier(&priv->notifier_block); + if (ret) { + dev_err(dev, "register notifier fail!\n"); + goto out_notify_fail; + } + dev_dbg(dev, "has not handle, register notifier!\n"); + } + + return 0; + +out_notify_fail: + (void)cancel_work_sync(&priv->service_task); +out_read_string_fail: + free_netdev(ndev); + return ret; +} + +static int hns_nic_dev_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct hns_nic_priv *priv = netdev_priv(ndev); + + if (ndev->reg_state != NETREG_UNINITIALIZED) + unregister_netdev(ndev); + + if (priv->ring_data) + hns_nic_uninit_ring_data(priv); + priv->ring_data = NULL; + + if (priv->phy) + phy_disconnect(priv->phy); + priv->phy = NULL; + + if (!IS_ERR_OR_NULL(priv->ae_handle)) + hnae_put_handle(priv->ae_handle); + priv->ae_handle = NULL; + if (priv->notifier_block.notifier_call) + hnae_unregister_notifier(&priv->notifier_block); + priv->notifier_block.notifier_call = NULL; + + set_bit(NIC_STATE_REMOVING, &priv->state); + (void)cancel_work_sync(&priv->service_task); + + free_netdev(ndev); + return 0; +} + +static const struct of_device_id hns_enet_of_match[] = { + {.compatible = "hisilicon,hns-nic-v1",}, + {.compatible = "hisilicon,hns-nic-v2",}, + {}, +}; + +MODULE_DEVICE_TABLE(of, hns_enet_of_match); + +static struct platform_driver hns_nic_dev_driver = { + .driver = { + .name = "hns-nic", + .owner = THIS_MODULE, + .of_match_table = hns_enet_of_match, + }, + .probe = hns_nic_dev_probe, + .remove = hns_nic_dev_remove, +}; + +module_platform_driver(hns_nic_dev_driver); + +MODULE_DESCRIPTION("HISILICON HNS Ethernet driver"); +MODULE_AUTHOR("Hisilicon, Inc."); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:hns-nic"); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h new file mode 100644 index 000000000000..dae0ed19ac6d --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HNS_ENET_H +#define __HNS_ENET_H + +#include <linux/netdevice.h> +#include <linux/of_net.h> +#include <linux/of_mdio.h> +#include <linux/timer.h> +#include <linux/workqueue.h> + +#include "hnae.h" + +enum hns_nic_state { + NIC_STATE_TESTING = 0, + NIC_STATE_RESETTING, + NIC_STATE_REINITING, + NIC_STATE_DOWN, + NIC_STATE_DISABLED, + NIC_STATE_REMOVING, + NIC_STATE_SERVICE_INITED, + NIC_STATE_SERVICE_SCHED, + NIC_STATE2_RESET_REQUESTED, + NIC_STATE_MAX +}; + +struct hns_nic_ring_data { + struct hnae_ring *ring; + struct napi_struct napi; + int queue_index; + int (*poll_one)(struct hns_nic_ring_data *, int, void *); + void (*ex_process)(struct hns_nic_ring_data *, struct sk_buff *); + void (*fini_process)(struct hns_nic_ring_data *); +}; + +struct hns_nic_priv { + const char *ae_name; + u32 enet_ver; + u32 port_id; + int phy_mode; + int phy_led_val; + struct phy_device *phy; + struct net_device *netdev; + struct device *dev; + struct hnae_handle *ae_handle; + + /* the cb for nic to manage the ring buffer, the first half of the + * array is for tx_ring and vice versa for the second half + */ + struct hns_nic_ring_data *ring_data; + + /* The most recently read link state */ + int link; + u64 tx_timeout_count; + + unsigned long state; + + struct timer_list service_timer; + + struct work_struct service_task; + + struct notifier_block notifier_block; +}; + +#define tx_ring_data(priv, idx) ((priv)->ring_data[idx]) +#define rx_ring_data(priv, idx) \ + ((priv)->ring_data[(priv)->ae_handle->q_num + (idx)]) + +void hns_ethtool_set_ops(struct net_device *ndev); +void hns_nic_net_reset(struct net_device *ndev); +void hns_nic_net_reinit(struct net_device *netdev); +int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h); +int hns_nic_net_xmit_hw(struct net_device *ndev, + struct sk_buff *skb, + struct hns_nic_ring_data *ring_data); + +#endif /**__HNS_ENET_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c new file mode 100644 index 000000000000..2550208cb22e --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -0,0 +1,1230 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/etherdevice.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "hns_enet.h" + +#define HNS_PHY_PAGE_MDIX 0 +#define HNS_PHY_PAGE_LED 3 +#define HNS_PHY_PAGE_COPPER 0 + +#define HNS_PHY_PAGE_REG 22 /* Page Selection Reg. */ +#define HNS_PHY_CSC_REG 16 /* Copper Specific Control Register */ +#define HNS_PHY_CSS_REG 17 /* Copper Specific Status Register */ +#define HNS_LED_FC_REG 16 /* LED Function Control Reg. */ +#define HNS_LED_PC_REG 17 /* LED Polarity Control Reg. */ + +#define HNS_LED_FORCE_ON 9 +#define HNS_LED_FORCE_OFF 8 + +#define HNS_CHIP_VERSION 660 +#define HNS_NET_STATS_CNT 26 + +#define PHY_MDIX_CTRL_S (5) +#define PHY_MDIX_CTRL_M (3 << PHY_MDIX_CTRL_S) + +#define PHY_MDIX_STATUS_B (6) +#define PHY_SPEED_DUP_RESOLVE_B (11) + +/** + *hns_nic_get_link - get current link status + *@net_dev: net_device + *retuen 0 - success , negative --fail + */ +static u32 hns_nic_get_link(struct net_device *net_dev) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + u32 link_stat = priv->link; + struct hnae_handle *h; + + assert(priv && priv->ae_handle); + h = priv->ae_handle; + + if (priv->phy) { + if (!genphy_update_link(priv->phy)) + link_stat = priv->phy->link; + else + link_stat = 0; + } + + if (h->dev && h->dev->ops && h->dev->ops->get_status) + link_stat = link_stat && h->dev->ops->get_status(h); + else + link_stat = 0; + + return link_stat; +} + +static void hns_get_mdix_mode(struct net_device *net_dev, + struct ethtool_cmd *cmd) +{ + int mdix_ctrl, mdix, retval, is_resolved; + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct phy_device *phy_dev = priv->phy; + + if (!phy_dev || !phy_dev->bus) { + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; + cmd->eth_tp_mdix = ETH_TP_MDI_INVALID; + return; + } + + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_MDIX); + + retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSC_REG); + mdix_ctrl = hnae_get_field(retval, PHY_MDIX_CTRL_M, PHY_MDIX_CTRL_S); + + retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSS_REG); + mdix = hnae_get_bit(retval, PHY_MDIX_STATUS_B); + is_resolved = hnae_get_bit(retval, PHY_SPEED_DUP_RESOLVE_B); + + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_COPPER); + + switch (mdix_ctrl) { + case 0x0: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI; + break; + case 0x1: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_X; + break; + case 0x3: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; + break; + default: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; + break; + } + + if (!is_resolved) + cmd->eth_tp_mdix = ETH_TP_MDI_INVALID; + else if (mdix) + cmd->eth_tp_mdix = ETH_TP_MDI_X; + else + cmd->eth_tp_mdix = ETH_TP_MDI; +} + +/** + *hns_nic_get_settings - implement ethtool get settings + *@net_dev: net_device + *@cmd: ethtool_cmd + *retuen 0 - success , negative --fail + */ +static int hns_nic_get_settings(struct net_device *net_dev, + struct ethtool_cmd *cmd) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_handle *h; + u32 link_stat; + int ret; + u8 duplex; + u16 speed; + + if (!priv || !priv->ae_handle) + return -ESRCH; + + h = priv->ae_handle; + if (!h->dev || !h->dev->ops || !h->dev->ops->get_info) + return -ESRCH; + + ret = h->dev->ops->get_info(h, NULL, &speed, &duplex); + if (ret < 0) { + netdev_err(net_dev, "%s get_info error!\n", __func__); + return -EINVAL; + } + + /* When there is no phy, autoneg is off. */ + cmd->autoneg = false; + ethtool_cmd_speed_set(cmd, speed); + cmd->duplex = duplex; + + if (priv->phy) + (void)phy_ethtool_gset(priv->phy, cmd); + + link_stat = hns_nic_get_link(net_dev); + if (!link_stat) { + ethtool_cmd_speed_set(cmd, (u32)SPEED_UNKNOWN); + cmd->duplex = DUPLEX_UNKNOWN; + } + + if (cmd->autoneg) + cmd->advertising |= ADVERTISED_Autoneg; + + cmd->supported |= h->if_support; + if (h->phy_if == PHY_INTERFACE_MODE_SGMII) { + cmd->supported |= SUPPORTED_TP; + cmd->advertising |= ADVERTISED_1000baseT_Full; + } else if (h->phy_if == PHY_INTERFACE_MODE_XGMII) { + cmd->supported |= SUPPORTED_FIBRE; + cmd->advertising |= ADVERTISED_10000baseKR_Full; + } + + if (h->port_type == HNAE_PORT_SERVICE) { + cmd->port = PORT_FIBRE; + cmd->supported |= SUPPORTED_Pause; + } else { + cmd->port = PORT_TP; + } + + cmd->transceiver = XCVR_EXTERNAL; + cmd->mdio_support = (ETH_MDIO_SUPPORTS_C45 | ETH_MDIO_SUPPORTS_C22); + hns_get_mdix_mode(net_dev, cmd); + + return 0; +} + +/** + *hns_nic_set_settings - implement ethtool set settings + *@net_dev: net_device + *@cmd: ethtool_cmd + *retuen 0 - success , negative --fail + */ +static int hns_nic_set_settings(struct net_device *net_dev, + struct ethtool_cmd *cmd) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_handle *h; + int link_stat; + u32 speed; + u8 duplex, autoneg; + + if (!netif_running(net_dev)) + return -ESRCH; + + if (!priv || !priv->ae_handle || !priv->ae_handle->dev || + !priv->ae_handle->dev->ops) + return -ENODEV; + + h = priv->ae_handle; + link_stat = hns_nic_get_link(net_dev); + duplex = cmd->duplex; + speed = ethtool_cmd_speed(cmd); + autoneg = cmd->autoneg; + + if (!link_stat) { + if (duplex != (u8)DUPLEX_UNKNOWN || speed != (u32)SPEED_UNKNOWN) + return -EINVAL; + + if (h->phy_if == PHY_INTERFACE_MODE_SGMII && h->phy_node) { + priv->phy->autoneg = autoneg; + return phy_start_aneg(priv->phy); + } + } + + if (h->phy_if == PHY_INTERFACE_MODE_XGMII) { + if (autoneg != AUTONEG_DISABLE) + return -EINVAL; + + if (speed != SPEED_10000 || duplex != DUPLEX_FULL) + return -EINVAL; + } else if (h->phy_if == PHY_INTERFACE_MODE_SGMII) { + if (!h->phy_node && autoneg != AUTONEG_DISABLE) + return -EINVAL; + + if (speed == SPEED_1000 && duplex == DUPLEX_HALF) + return -EINVAL; + + if (speed != SPEED_10 && speed != SPEED_100 && + speed != SPEED_1000) + return -EINVAL; + } else { + netdev_err(net_dev, "Not supported!"); + return -ENOTSUPP; + } + + if (priv->phy) { + return phy_ethtool_sset(priv->phy, cmd); + } else if (h->dev->ops->adjust_link && link_stat) { + h->dev->ops->adjust_link(h, speed, duplex); + return 0; + } + netdev_err(net_dev, "Not supported!"); + return -ENOTSUPP; +} + +static const char hns_nic_test_strs[][ETH_GSTRING_LEN] = { + "Mac Loopback test", + "Serdes Loopback test", + "Phy Loopback test" +}; + +static int hns_nic_config_phy_loopback(struct phy_device *phy_dev, u8 en) +{ +#define COPPER_CONTROL_REG 0 +#define PHY_LOOP_BACK BIT(14) + u16 val = 0; + + if (phy_dev->is_c45) /* c45 branch adding for XGE PHY */ + return -ENOTSUPP; + + if (en) { + /* speed : 1000M */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, 2); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 21, 0x1046); + /* Force Master */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 9, 0x1F00); + /* Soft-reset */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 0, 0x9140); + /* If autoneg disabled,two soft-reset operations */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 0, 0x9140); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0xFA); + + /* Default is 0x0400 */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 1, 0x418); + + /* Force 1000M Link, Default is 0x0200 */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 7, 0x20C); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0); + + /* Enable MAC loop-back */ + val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG); + val |= PHY_LOOP_BACK; + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG, val); + } else { + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0xFA); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 1, 0x400); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 7, 0x200); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0); + + val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG); + val &= ~PHY_LOOP_BACK; + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG, val); + } + return 0; +} + +static int __lb_setup(struct net_device *ndev, + enum hnae_loop loop) +{ + int ret = 0; + struct hns_nic_priv *priv = netdev_priv(ndev); + struct phy_device *phy_dev = priv->phy; + struct hnae_handle *h = priv->ae_handle; + + switch (loop) { + case MAC_INTERNALLOOP_PHY: + if ((phy_dev) && (!phy_dev->is_c45)) + ret = hns_nic_config_phy_loopback(phy_dev, 0x1); + break; + case MAC_INTERNALLOOP_MAC: + if ((h->dev->ops->set_loopback) && + (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII)) + ret = h->dev->ops->set_loopback(h, loop, 0x1); + break; + case MAC_INTERNALLOOP_SERDES: + if (h->dev->ops->set_loopback) + ret = h->dev->ops->set_loopback(h, loop, 0x1); + break; + case MAC_LOOP_NONE: + if ((phy_dev) && (!phy_dev->is_c45)) + ret |= hns_nic_config_phy_loopback(phy_dev, 0x0); + + if (h->dev->ops->set_loopback) { + if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII) + ret |= h->dev->ops->set_loopback(h, + MAC_INTERNALLOOP_MAC, 0x0); + + ret |= h->dev->ops->set_loopback(h, + MAC_INTERNALLOOP_SERDES, 0x0); + } + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int __lb_up(struct net_device *ndev, + enum hnae_loop loop_mode) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int speed, duplex; + int ret; + + hns_nic_net_reset(ndev); + + if (priv->phy) { + phy_disconnect(priv->phy); + msleep(100); + + ret = hns_nic_init_phy(ndev, h); + if (ret) + return ret; + } + + ret = __lb_setup(ndev, loop_mode); + if (ret) + return ret; + + msleep(100); + + ret = h->dev->ops->start ? h->dev->ops->start(h) : 0; + if (ret) + return ret; + + if (priv->phy) + phy_start(priv->phy); + + /* link adjust duplex*/ + if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII) + speed = 1000; + else + speed = 10000; + duplex = 1; + + h->dev->ops->adjust_link(h, speed, duplex); + + return 0; +} + +static void __lb_other_process(struct hns_nic_ring_data *ring_data, + struct sk_buff *skb) +{ + struct net_device *ndev; + struct hnae_ring *ring; + struct netdev_queue *dev_queue; + struct sk_buff *new_skb; + unsigned int frame_size; + int check_ok; + u32 i; + char buff[33]; /* 32B data and the last character '\0' */ + + if (!ring_data) { /* Just for doing create frame*/ + frame_size = skb->len; + memset(skb->data, 0xFF, frame_size); + frame_size &= ~1ul; + memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1); + memset(&skb->data[frame_size / 2 + 10], 0xBE, + frame_size / 2 - 11); + memset(&skb->data[frame_size / 2 + 12], 0xAF, + frame_size / 2 - 13); + return; + } + + ring = ring_data->ring; + ndev = ring_data->napi.dev; + if (is_tx_ring(ring)) { /* for tx queue reset*/ + dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); + netdev_tx_reset_queue(dev_queue); + return; + } + + frame_size = skb->len; + frame_size &= ~1ul; + /* for mutl buffer*/ + new_skb = skb_copy(skb, GFP_ATOMIC); + dev_kfree_skb_any(skb); + skb = new_skb; + + check_ok = 0; + if (*(skb->data + 10) == 0xFF) { /* for rx check frame*/ + if ((*(skb->data + frame_size / 2 + 10) == 0xBE) && + (*(skb->data + frame_size / 2 + 12) == 0xAF)) + check_ok = 1; + } + + if (check_ok) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += skb->len; + } else { + ndev->stats.rx_frame_errors++; + for (i = 0; i < skb->len; i++) { + snprintf(buff + i % 16 * 2, 3, /* tailing \0*/ + "%02x", *(skb->data + i)); + if ((i % 16 == 15) || (i == skb->len - 1)) + pr_info("%s\n", buff); + } + } + dev_kfree_skb_any(skb); +} + +static int __lb_clean_rings(struct hns_nic_priv *priv, + int ringid0, int ringid1, int budget) +{ + int i, ret; + struct hns_nic_ring_data *ring_data; + struct net_device *ndev = priv->netdev; + unsigned long rx_packets = ndev->stats.rx_packets; + unsigned long rx_bytes = ndev->stats.rx_bytes; + unsigned long rx_frame_errors = ndev->stats.rx_frame_errors; + + for (i = ringid0; i <= ringid1; i++) { + ring_data = &priv->ring_data[i]; + (void)ring_data->poll_one(ring_data, + budget, __lb_other_process); + } + ret = (int)(ndev->stats.rx_packets - rx_packets); + ndev->stats.rx_packets = rx_packets; + ndev->stats.rx_bytes = rx_bytes; + ndev->stats.rx_frame_errors = rx_frame_errors; + return ret; +} + +/** + * nic_run_loopback_test - run loopback test + * @nic_dev: net device + * @loopback_type: loopback type + */ +static int __lb_run_test(struct net_device *ndev, + enum hnae_loop loop_mode) +{ +#define NIC_LB_TEST_PKT_NUM_PER_CYCLE 1 +#define NIC_LB_TEST_RING_ID 0 +#define NIC_LB_TEST_FRAME_SIZE 128 +/* nic loopback test err */ +#define NIC_LB_TEST_NO_MEM_ERR 1 +#define NIC_LB_TEST_TX_CNT_ERR 2 +#define NIC_LB_TEST_RX_CNT_ERR 3 +#define NIC_LB_TEST_RX_PKG_ERR 4 + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int i, j, lc, good_cnt, ret_val = 0; + unsigned int size; + netdev_tx_t tx_ret_val; + struct sk_buff *skb; + + size = NIC_LB_TEST_FRAME_SIZE; + /* allocate test skb */ + skb = alloc_skb(size, GFP_KERNEL); + if (!skb) + return NIC_LB_TEST_NO_MEM_ERR; + + /* place data into test skb */ + (void)skb_put(skb, size); + __lb_other_process(NULL, skb); + skb->queue_mapping = NIC_LB_TEST_RING_ID; + + lc = 1; + for (j = 0; j < lc; j++) { + /* reset count of good packets */ + good_cnt = 0; + /* place 64 packets on the transmit queue*/ + for (i = 0; i < NIC_LB_TEST_PKT_NUM_PER_CYCLE; i++) { + (void)skb_get(skb); + + tx_ret_val = (netdev_tx_t)hns_nic_net_xmit_hw( + ndev, skb, + &tx_ring_data(priv, skb->queue_mapping)); + if (tx_ret_val == NETDEV_TX_OK) + good_cnt++; + else + break; + } + if (good_cnt != NIC_LB_TEST_PKT_NUM_PER_CYCLE) { + ret_val = NIC_LB_TEST_TX_CNT_ERR; + dev_err(priv->dev, "%s sent fail, cnt=0x%x, budget=0x%x\n", + hns_nic_test_strs[loop_mode], good_cnt, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + break; + } + + /* allow 100 milliseconds for packets to go from Tx to Rx */ + msleep(100); + + good_cnt = __lb_clean_rings(priv, + h->q_num, h->q_num * 2 - 1, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + if (good_cnt != NIC_LB_TEST_PKT_NUM_PER_CYCLE) { + ret_val = NIC_LB_TEST_RX_CNT_ERR; + dev_err(priv->dev, "%s recv fail, cnt=0x%x, budget=0x%x\n", + hns_nic_test_strs[loop_mode], good_cnt, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + break; + } + (void)__lb_clean_rings(priv, + NIC_LB_TEST_RING_ID, NIC_LB_TEST_RING_ID, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + } + + /* free the original skb */ + kfree_skb(skb); + + return ret_val; +} + +static int __lb_down(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int ret; + + ret = __lb_setup(ndev, MAC_LOOP_NONE); + if (ret) + netdev_err(ndev, "%s: __lb_setup return error(%d)!\n", + __func__, + ret); + + if (priv->phy) + phy_stop(priv->phy); + + if (h->dev->ops->stop) + h->dev->ops->stop(h); + + usleep_range(10000, 20000); + (void)__lb_clean_rings(priv, 0, h->q_num - 1, 256); + + hns_nic_net_reset(ndev); + + return 0; +} + +/** + * hns_nic_self_test - self test + * @dev: net device + * @eth_test: test cmd + * @data: test result + */ +static void hns_nic_self_test(struct net_device *ndev, + struct ethtool_test *eth_test, u64 *data) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + bool if_running = netif_running(ndev); +#define SELF_TEST_TPYE_NUM 3 + int st_param[SELF_TEST_TPYE_NUM][2]; + int i; + int test_index = 0; + + st_param[0][0] = MAC_INTERNALLOOP_MAC; /* XGE not supported lb */ + st_param[0][1] = (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII); + st_param[1][0] = MAC_INTERNALLOOP_SERDES; + st_param[1][1] = 1; /*serdes must exist*/ + st_param[2][0] = MAC_INTERNALLOOP_PHY; /* only supporte phy node*/ + st_param[2][1] = ((!!(priv->ae_handle->phy_node)) && + (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII)); + + if (eth_test->flags == ETH_TEST_FL_OFFLINE) { + set_bit(NIC_STATE_TESTING, &priv->state); + + if (if_running) + (void)dev_close(ndev); + + for (i = 0; i < SELF_TEST_TPYE_NUM; i++) { + if (!st_param[i][1]) + continue; /* NEXT testing */ + + data[test_index] = __lb_up(ndev, + (enum hnae_loop)st_param[i][0]); + if (!data[test_index]) { + data[test_index] = __lb_run_test( + ndev, (enum hnae_loop)st_param[i][0]); + (void)__lb_down(ndev); + } + + if (data[test_index]) + eth_test->flags |= ETH_TEST_FL_FAILED; + + test_index++; + } + + hns_nic_net_reset(priv->netdev); + + clear_bit(NIC_STATE_TESTING, &priv->state); + + if (if_running) + (void)dev_open(ndev); + } + /* Online tests aren't run; pass by default */ + + (void)msleep_interruptible(4 * 1000); +} + +/** + * hns_nic_get_drvinfo - get net driver info + * @dev: net device + * @drvinfo: driver info + */ +static void hns_nic_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *drvinfo) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + + assert(priv); + + strncpy(drvinfo->version, HNAE_DRIVER_VERSION, + sizeof(drvinfo->version)); + drvinfo->version[sizeof(drvinfo->version) - 1] = '\0'; + + strncpy(drvinfo->driver, HNAE_DRIVER_NAME, sizeof(drvinfo->driver)); + drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0'; + + strncpy(drvinfo->bus_info, priv->dev->bus->name, + sizeof(drvinfo->bus_info)); + drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0'; + + strncpy(drvinfo->fw_version, "N/A", ETHTOOL_FWVERS_LEN); + drvinfo->eedump_len = 0; +} + +/** + * hns_get_ringparam - get ring parameter + * @dev: net device + * @param: ethtool parameter + */ +void hns_get_ringparam(struct net_device *net_dev, + struct ethtool_ringparam *param) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + struct hnae_queue *queue; + u32 uplimit = 0; + + queue = priv->ae_handle->qs[0]; + ops = priv->ae_handle->dev->ops; + + if (ops->get_ring_bdnum_limit) + ops->get_ring_bdnum_limit(queue, &uplimit); + + param->rx_max_pending = uplimit; + param->tx_max_pending = uplimit; + param->rx_pending = queue->rx_ring.desc_num; + param->tx_pending = queue->tx_ring.desc_num; +} + +/** + * hns_get_pauseparam - get pause parameter + * @dev: net device + * @param: pause parameter + */ +static void hns_get_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *param) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + ops = priv->ae_handle->dev->ops; + + if (ops->get_pauseparam) + ops->get_pauseparam(priv->ae_handle, ¶m->autoneg, + ¶m->rx_pause, ¶m->tx_pause); +} + +/** + * hns_set_pauseparam - set pause parameter + * @dev: net device + * @param: pause parameter + * + * Return 0 on success, negative on failure + */ +static int hns_set_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *param) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_handle *h; + struct hnae_ae_ops *ops; + + assert(priv || priv->ae_handle); + + h = priv->ae_handle; + ops = h->dev->ops; + + if (!ops->set_pauseparam) + return -ESRCH; + + return ops->set_pauseparam(priv->ae_handle, param->autoneg, + param->rx_pause, param->tx_pause); +} + +/** + * hns_get_coalesce - get coalesce info. + * @dev: net device + * @ec: coalesce info. + * + * Return 0 on success, negative on failure. + */ +static int hns_get_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *ec) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + ops = priv->ae_handle->dev->ops; + + ec->use_adaptive_rx_coalesce = 1; + ec->use_adaptive_tx_coalesce = 1; + + if ((!ops->get_coalesce_usecs) || + (!ops->get_rx_max_coalesced_frames)) + return -ESRCH; + + ops->get_coalesce_usecs(priv->ae_handle, + &ec->tx_coalesce_usecs, + &ec->rx_coalesce_usecs); + + ops->get_rx_max_coalesced_frames( + priv->ae_handle, + &ec->tx_max_coalesced_frames, + &ec->rx_max_coalesced_frames); + + return 0; +} + +/** + * hns_set_coalesce - set coalesce info. + * @dev: net device + * @ec: coalesce info. + * + * Return 0 on success, negative on failure. + */ +static int hns_set_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *ec) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + int ret; + + assert(priv || priv->ae_handle); + + ops = priv->ae_handle->dev->ops; + + if (ec->tx_coalesce_usecs != ec->rx_coalesce_usecs) + return -EINVAL; + + if (ec->rx_max_coalesced_frames != ec->tx_max_coalesced_frames) + return -EINVAL; + + if ((!ops->set_coalesce_usecs) || + (!ops->set_coalesce_frames)) + return -ESRCH; + + ops->set_coalesce_usecs(priv->ae_handle, + ec->rx_coalesce_usecs); + + ret = ops->set_coalesce_frames( + priv->ae_handle, + ec->rx_max_coalesced_frames); + + return ret; +} + +/** + * hns_get_channels - get channel info. + * @dev: net device + * @ch: channel info. + */ +void hns_get_channels(struct net_device *net_dev, struct ethtool_channels *ch) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + + ch->max_rx = priv->ae_handle->q_num; + ch->max_tx = priv->ae_handle->q_num; + + ch->rx_count = priv->ae_handle->q_num; + ch->tx_count = priv->ae_handle->q_num; +} + +/** + * get_ethtool_stats - get detail statistics. + * @dev: net device + * @stats: statistics info. + * @data: statistics data. + */ +void hns_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + u64 *p = data; + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + const struct rtnl_link_stats64 *net_stats; + struct rtnl_link_stats64 temp; + + if (!h->dev->ops->get_stats || !h->dev->ops->update_stats) { + netdev_err(netdev, "get_stats or update_stats is null!\n"); + return; + } + + h->dev->ops->update_stats(h, &netdev->stats); + + net_stats = dev_get_stats(netdev, &temp); + + /* get netdev statistics */ + p[0] = net_stats->rx_packets; + p[1] = net_stats->tx_packets; + p[2] = net_stats->rx_bytes; + p[3] = net_stats->tx_bytes; + p[4] = net_stats->rx_errors; + p[5] = net_stats->tx_errors; + p[6] = net_stats->rx_dropped; + p[7] = net_stats->tx_dropped; + p[8] = net_stats->multicast; + p[9] = net_stats->collisions; + p[10] = net_stats->rx_over_errors; + p[11] = net_stats->rx_crc_errors; + p[12] = net_stats->rx_frame_errors; + p[13] = net_stats->rx_fifo_errors; + p[14] = net_stats->rx_missed_errors; + p[15] = net_stats->tx_aborted_errors; + p[16] = net_stats->tx_carrier_errors; + p[17] = net_stats->tx_fifo_errors; + p[18] = net_stats->tx_heartbeat_errors; + p[19] = net_stats->rx_length_errors; + p[20] = net_stats->tx_window_errors; + p[21] = net_stats->rx_compressed; + p[22] = net_stats->tx_compressed; + + p[23] = netdev->rx_dropped.counter; + p[24] = netdev->tx_dropped.counter; + + p[25] = priv->tx_timeout_count; + + /* get driver statistics */ + h->dev->ops->get_stats(h, &p[26]); +} + +/** + * get_strings: Return a set of strings that describe the requested objects + * @dev: net device + * @stats: string set ID. + * @data: objects data. + */ +void hns_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + char *buff = (char *)data; + + if (!h->dev->ops->get_strings) { + netdev_err(netdev, "h->dev->ops->get_strings is null!\n"); + return; + } + + if (stringset == ETH_SS_TEST) { + if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII) { + memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_MAC], + ETH_GSTRING_LEN); + buff += ETH_GSTRING_LEN; + } + memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_SERDES], + ETH_GSTRING_LEN); + buff += ETH_GSTRING_LEN; + if ((priv->phy) && (!priv->phy->is_c45)) + memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_PHY], + ETH_GSTRING_LEN); + + } else { + snprintf(buff, ETH_GSTRING_LEN, "rx_packets"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_packets"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_bytes"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_bytes"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_dropped"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_dropped"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "multicast"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "collisions"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_over_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_crc_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_frame_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_fifo_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_missed_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_aborted_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_carrier_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_fifo_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_heartbeat_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_length_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_window_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_compressed"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_compressed"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "netdev_rx_dropped"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "netdev_tx_dropped"); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "netdev_tx_timeout"); + buff = buff + ETH_GSTRING_LEN; + + h->dev->ops->get_strings(h, stringset, (u8 *)buff); + } +} + +/** + * nic_get_sset_count - get string set count witch returned by nic_get_strings. + * @dev: net device + * @stringset: string set index, 0: self test string; 1: statistics string. + * + * Return string set count. + */ +int hns_get_sset_count(struct net_device *netdev, int stringset) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + struct hnae_ae_ops *ops = h->dev->ops; + + if (!ops->get_sset_count) { + netdev_err(netdev, "get_sset_count is null!\n"); + return -EOPNOTSUPP; + } + if (stringset == ETH_SS_TEST) { + u32 cnt = (sizeof(hns_nic_test_strs) / ETH_GSTRING_LEN); + + if (priv->ae_handle->phy_if == PHY_INTERFACE_MODE_XGMII) + cnt--; + + if ((!priv->phy) || (priv->phy->is_c45)) + cnt--; + + return cnt; + } else { + return (HNS_NET_STATS_CNT + ops->get_sset_count(h, stringset)); + } +} + +/** + * hns_phy_led_set - set phy LED status. + * @dev: net device + * @value: LED state. + * + * Return 0 on success, negative on failure. + */ +int hns_phy_led_set(struct net_device *netdev, int value) +{ + int retval; + struct hns_nic_priv *priv = netdev_priv(netdev); + struct phy_device *phy_dev = priv->phy; + + if (!phy_dev->bus) { + netdev_err(netdev, "phy_dev->bus is null!\n"); + return -EINVAL; + } + retval = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, HNS_PHY_PAGE_LED); + retval = mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_LED_FC_REG, + value); + retval = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER); + if (retval) { + netdev_err(netdev, "mdiobus_write fail !\n"); + return retval; + } + return 0; +} + +/** + * nic_set_phys_id - set phy identify LED. + * @dev: net device + * @state: LED state. + * + * Return 0 on success, negative on failure. + */ +int hns_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + struct phy_device *phy_dev = priv->phy; + int ret; + + if (phy_dev) + switch (state) { + case ETHTOOL_ID_ACTIVE: + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_LED); + if (ret) + return ret; + + priv->phy_led_val = (u16)mdiobus_read(phy_dev->bus, + phy_dev->addr, + HNS_LED_FC_REG); + + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_COPPER); + if (ret) + return ret; + return 2; + case ETHTOOL_ID_ON: + ret = hns_phy_led_set(netdev, HNS_LED_FORCE_ON); + if (ret) + return ret; + break; + case ETHTOOL_ID_OFF: + ret = hns_phy_led_set(netdev, HNS_LED_FORCE_OFF); + if (ret) + return ret; + break; + case ETHTOOL_ID_INACTIVE: + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_LED); + if (ret) + return ret; + + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_LED_FC_REG, priv->phy_led_val); + if (ret) + return ret; + + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_COPPER); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + else + switch (state) { + case ETHTOOL_ID_ACTIVE: + return h->dev->ops->set_led_id(h, HNAE_LED_ACTIVE); + case ETHTOOL_ID_ON: + return h->dev->ops->set_led_id(h, HNAE_LED_ON); + case ETHTOOL_ID_OFF: + return h->dev->ops->set_led_id(h, HNAE_LED_OFF); + case ETHTOOL_ID_INACTIVE: + return h->dev->ops->set_led_id(h, HNAE_LED_INACTIVE); + default: + return -EINVAL; + } + + return 0; +} + +/** + * hns_get_regs - get net device register + * @dev: net device + * @cmd: ethtool cmd + * @date: register data + */ +void hns_get_regs(struct net_device *net_dev, struct ethtool_regs *cmd, + void *data) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + assert(priv || priv->ae_handle); + + ops = priv->ae_handle->dev->ops; + + cmd->version = HNS_CHIP_VERSION; + if (!ops->get_regs) { + netdev_err(net_dev, "ops->get_regs is null!\n"); + return; + } + ops->get_regs(priv->ae_handle, data); +} + +/** + * nic_get_regs_len - get total register len. + * @dev: net device + * + * Return total register len. + */ +static int hns_get_regs_len(struct net_device *net_dev) +{ + u32 reg_num; + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + assert(priv || priv->ae_handle); + + ops = priv->ae_handle->dev->ops; + if (!ops->get_regs_len) { + netdev_err(net_dev, "ops->get_regs_len is null!\n"); + return -EOPNOTSUPP; + } + + reg_num = ops->get_regs_len(priv->ae_handle); + if (reg_num > 0) + return reg_num * sizeof(u32); + else + return reg_num; /* error code */ +} + +/** + * hns_nic_nway_reset - nway reset + * @dev: net device + * + * Return 0 on success, negative on failure + */ +static int hns_nic_nway_reset(struct net_device *netdev) +{ + int ret = 0; + struct hns_nic_priv *priv = netdev_priv(netdev); + struct phy_device *phy = priv->phy; + + if (netif_running(netdev)) { + if (phy) + ret = genphy_restart_aneg(phy); + } + + return ret; +} + +static struct ethtool_ops hns_ethtool_ops = { + .get_drvinfo = hns_nic_get_drvinfo, + .get_link = hns_nic_get_link, + .get_settings = hns_nic_get_settings, + .set_settings = hns_nic_set_settings, + .get_ringparam = hns_get_ringparam, + .get_pauseparam = hns_get_pauseparam, + .set_pauseparam = hns_set_pauseparam, + .get_coalesce = hns_get_coalesce, + .set_coalesce = hns_set_coalesce, + .get_channels = hns_get_channels, + .self_test = hns_nic_self_test, + .get_strings = hns_get_strings, + .get_sset_count = hns_get_sset_count, + .get_ethtool_stats = hns_get_ethtool_stats, + .set_phys_id = hns_set_phys_id, + .get_regs_len = hns_get_regs_len, + .get_regs = hns_get_regs, + .nway_reset = hns_nic_nway_reset, +}; + +void hns_ethtool_set_ops(struct net_device *ndev) +{ + ndev->ethtool_ops = &hns_ethtool_ops; +} diff --git a/drivers/net/ethernet/hisilicon/hns_mdio.c b/drivers/net/ethernet/hisilicon/hns_mdio.c new file mode 100644 index 000000000000..e4ec52ae61ff --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns_mdio.c @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/errno.h> +#include <linux/etherdevice.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/netdevice.h> +#include <linux/of_address.h> +#include <linux/of.h> +#include <linux/of_mdio.h> +#include <linux/of_platform.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <linux/spinlock_types.h> + +#define MDIO_DRV_NAME "Hi-HNS_MDIO" +#define MDIO_BUS_NAME "Hisilicon MII Bus" +#define MDIO_DRV_VERSION "1.3.0" +#define MDIO_COPYRIGHT "Copyright(c) 2015 Huawei Corporation." +#define MDIO_DRV_STRING MDIO_BUS_NAME +#define MDIO_DEFAULT_DEVICE_DESCR MDIO_BUS_NAME + +#define MDIO_CTL_DEV_ADDR(x) (x & 0x1f) +#define MDIO_CTL_PORT_ADDR(x) ((x & 0x1f) << 5) + +#define MDIO_TIMEOUT 1000000 + +struct hns_mdio_device { + void *vbase; /* mdio reg base address */ + void *sys_vbase; +}; + +/* mdio reg */ +#define MDIO_COMMAND_REG 0x0 +#define MDIO_ADDR_REG 0x4 +#define MDIO_WDATA_REG 0x8 +#define MDIO_RDATA_REG 0xc +#define MDIO_STA_REG 0x10 + +/* cfg phy bit map */ +#define MDIO_CMD_DEVAD_M 0x1f +#define MDIO_CMD_DEVAD_S 0 +#define MDIO_CMD_PRTAD_M 0x1f +#define MDIO_CMD_PRTAD_S 5 +#define MDIO_CMD_OP_M 0x3 +#define MDIO_CMD_OP_S 10 +#define MDIO_CMD_ST_M 0x3 +#define MDIO_CMD_ST_S 12 +#define MDIO_CMD_START_B 14 + +#define MDIO_ADDR_DATA_M 0xffff +#define MDIO_ADDR_DATA_S 0 + +#define MDIO_WDATA_DATA_M 0xffff +#define MDIO_WDATA_DATA_S 0 + +#define MDIO_RDATA_DATA_M 0xffff +#define MDIO_RDATA_DATA_S 0 + +#define MDIO_STATE_STA_B 0 + +enum mdio_st_clause { + MDIO_ST_CLAUSE_45 = 0, + MDIO_ST_CLAUSE_22 +}; + +enum mdio_c22_op_seq { + MDIO_C22_WRITE = 1, + MDIO_C22_READ = 2 +}; + +enum mdio_c45_op_seq { + MDIO_C45_WRITE_ADDR = 0, + MDIO_C45_WRITE_DATA, + MDIO_C45_READ_INCREMENT, + MDIO_C45_READ +}; + +/* peri subctrl reg */ +#define MDIO_SC_CLK_EN 0x338 +#define MDIO_SC_CLK_DIS 0x33C +#define MDIO_SC_RESET_REQ 0xA38 +#define MDIO_SC_RESET_DREQ 0xA3C +#define MDIO_SC_CTRL 0x2010 +#define MDIO_SC_CLK_ST 0x531C +#define MDIO_SC_RESET_ST 0x5A1C + +static void mdio_write_reg(void *base, u32 reg, u32 value) +{ + u8 __iomem *reg_addr = (u8 __iomem *)base; + + writel_relaxed(value, reg_addr + reg); +} + +#define MDIO_WRITE_REG(a, reg, value) \ + mdio_write_reg((a)->vbase, (reg), (value)) + +static u32 mdio_read_reg(void *base, u32 reg) +{ + u8 __iomem *reg_addr = (u8 __iomem *)base; + + return readl_relaxed(reg_addr + reg); +} + +#define mdio_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~((mask) << (shift))); \ + (origin) |= (((val) & (mask)) << (shift)); \ + } while (0) + +#define mdio_get_field(origin, mask, shift) (((origin) >> (shift)) & (mask)) + +static void mdio_set_reg_field(void *base, u32 reg, u32 mask, u32 shift, + u32 val) +{ + u32 origin = mdio_read_reg(base, reg); + + mdio_set_field(origin, mask, shift, val); + mdio_write_reg(base, reg, origin); +} + +#define MDIO_SET_REG_FIELD(dev, reg, mask, shift, val) \ + mdio_set_reg_field((dev)->vbase, (reg), (mask), (shift), (val)) + +static u32 mdio_get_reg_field(void *base, u32 reg, u32 mask, u32 shift) +{ + u32 origin; + + origin = mdio_read_reg(base, reg); + return mdio_get_field(origin, mask, shift); +} + +#define MDIO_GET_REG_FIELD(dev, reg, mask, shift) \ + mdio_get_reg_field((dev)->vbase, (reg), (mask), (shift)) + +#define MDIO_GET_REG_BIT(dev, reg, bit) \ + mdio_get_reg_field((dev)->vbase, (reg), 0x1ull, (bit)) + +#define MDIO_CHECK_SET_ST 1 +#define MDIO_CHECK_CLR_ST 0 + +static int mdio_sc_cfg_reg_write(struct hns_mdio_device *mdio_dev, + u32 cfg_reg, u32 set_val, + u32 st_reg, u32 st_msk, u8 check_st) +{ + u32 time_cnt; + u32 reg_value; + + mdio_write_reg((void *)mdio_dev->sys_vbase, cfg_reg, set_val); + + for (time_cnt = MDIO_TIMEOUT; time_cnt; time_cnt--) { + reg_value = mdio_read_reg((void *)mdio_dev->sys_vbase, st_reg); + reg_value &= st_msk; + if ((!!check_st) == (!!reg_value)) + break; + } + + if ((!!check_st) != (!!reg_value)) + return -EBUSY; + + return 0; +} + +static int hns_mdio_wait_ready(struct mii_bus *bus) +{ + struct hns_mdio_device *mdio_dev = bus->priv; + int i; + u32 cmd_reg_value = 1; + + /* waitting for MDIO_COMMAND_REG 's mdio_start==0 */ + /* after that can do read or write*/ + for (i = 0; cmd_reg_value; i++) { + cmd_reg_value = MDIO_GET_REG_BIT(mdio_dev, + MDIO_COMMAND_REG, + MDIO_CMD_START_B); + if (i == MDIO_TIMEOUT) + return -ETIMEDOUT; + } + + return 0; +} + +static void hns_mdio_cmd_write(struct hns_mdio_device *mdio_dev, + u8 is_c45, u8 op, u8 phy_id, u16 cmd) +{ + u32 cmd_reg_value; + u8 st = is_c45 ? MDIO_ST_CLAUSE_45 : MDIO_ST_CLAUSE_22; + + cmd_reg_value = st << MDIO_CMD_ST_S; + cmd_reg_value |= op << MDIO_CMD_OP_S; + cmd_reg_value |= + (phy_id & MDIO_CMD_PRTAD_M) << MDIO_CMD_PRTAD_S; + cmd_reg_value |= (cmd & MDIO_CMD_DEVAD_M) << MDIO_CMD_DEVAD_S; + cmd_reg_value |= 1 << MDIO_CMD_START_B; + + MDIO_WRITE_REG(mdio_dev, MDIO_COMMAND_REG, cmd_reg_value); +} + +/** + * hns_mdio_write - access phy register + * @bus: mdio bus + * @phy_id: phy id + * @regnum: register num + * @value: register value + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_write(struct mii_bus *bus, + int phy_id, int regnum, u16 data) +{ + int ret; + struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; + u8 devad = ((regnum >> 16) & 0x1f); + u8 is_c45 = !!(regnum & MII_ADDR_C45); + u16 reg = (u16)(regnum & 0xffff); + u8 op; + u16 cmd_reg_cfg; + + dev_dbg(&bus->dev, "mdio write %s,base is %p\n", + bus->id, mdio_dev->vbase); + dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x, write data=%d\n", + phy_id, is_c45, devad, reg, data); + + /* wait for ready */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + if (!is_c45) { + cmd_reg_cfg = reg; + op = MDIO_C22_WRITE; + } else { + /* config the cmd-reg to write addr*/ + MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, + MDIO_ADDR_DATA_S, reg); + + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C45_WRITE_ADDR, phy_id, devad); + + /* check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + /* config the data needed writing */ + cmd_reg_cfg = devad; + op = MDIO_C45_WRITE_ADDR; + } + + MDIO_SET_REG_FIELD(mdio_dev, MDIO_WDATA_REG, MDIO_WDATA_DATA_M, + MDIO_WDATA_DATA_S, data); + + hns_mdio_cmd_write(mdio_dev, is_c45, op, phy_id, cmd_reg_cfg); + + return 0; +} + +/** + * hns_mdio_read - access phy register + * @bus: mdio bus + * @phy_id: phy id + * @regnum: register num + * @value: register value + * + * Return phy register value + */ +static int hns_mdio_read(struct mii_bus *bus, int phy_id, int regnum) +{ + int ret; + u16 reg_val = 0; + u8 devad = ((regnum >> 16) & 0x1f); + u8 is_c45 = !!(regnum & MII_ADDR_C45); + u16 reg = (u16)(regnum & 0xffff); + struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; + + dev_dbg(&bus->dev, "mdio read %s,base is %p\n", + bus->id, mdio_dev->vbase); + dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x!\n", + phy_id, is_c45, devad, reg); + + /* Step 1: wait for ready */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + if (!is_c45) { + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C22_READ, phy_id, reg); + } else { + MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, + MDIO_ADDR_DATA_S, reg); + + /* Step 2; config the cmd-reg to write addr*/ + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C45_WRITE_ADDR, phy_id, devad); + + /* Step 3: check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C45_WRITE_ADDR, phy_id, devad); + } + + /* Step 5: waitting for MDIO_COMMAND_REG 's mdio_start==0,*/ + /* check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + reg_val = MDIO_GET_REG_BIT(mdio_dev, MDIO_STA_REG, MDIO_STATE_STA_B); + if (reg_val) { + dev_err(&bus->dev, " ERROR! MDIO Read failed!\n"); + return -EBUSY; + } + + /* Step 6; get out data*/ + reg_val = (u16)MDIO_GET_REG_FIELD(mdio_dev, MDIO_RDATA_REG, + MDIO_RDATA_DATA_M, MDIO_RDATA_DATA_S); + + return reg_val; +} + +/** + * hns_mdio_reset - reset mdio bus + * @bus: mdio bus + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_reset(struct mii_bus *bus) +{ + struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; + int ret; + + if (!mdio_dev->sys_vbase) { + dev_err(&bus->dev, "mdio sys ctl reg has not maped\n"); + return -ENODEV; + } + + /*1. reset req, and read reset st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_RESET_REQ, 0x1, + MDIO_SC_RESET_ST, 0x1, + MDIO_CHECK_SET_ST); + if (ret) { + dev_err(&bus->dev, "MDIO reset fail\n"); + return ret; + } + + /*2. dis clk, and read clk st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_CLK_DIS, + 0x1, MDIO_SC_CLK_ST, 0x1, + MDIO_CHECK_CLR_ST); + if (ret) { + dev_err(&bus->dev, "MDIO dis clk fail\n"); + return ret; + } + + /*3. reset dreq, and read reset st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_RESET_DREQ, 0x1, + MDIO_SC_RESET_ST, 0x1, + MDIO_CHECK_CLR_ST); + if (ret) { + dev_err(&bus->dev, "MDIO dis clk fail\n"); + return ret; + } + + /*4. en clk, and read clk st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_CLK_EN, + 0x1, MDIO_SC_CLK_ST, 0x1, + MDIO_CHECK_SET_ST); + if (ret) + dev_err(&bus->dev, "MDIO en clk fail\n"); + + return ret; +} + +/** + * hns_mdio_bus_name - get mdio bus name + * @name: mdio bus name + * @np: mdio device node pointer + */ +static void hns_mdio_bus_name(char *name, struct device_node *np) +{ + const u32 *addr; + u64 taddr = OF_BAD_ADDR; + + addr = of_get_address(np, 0, NULL, NULL); + if (addr) + taddr = of_translate_address(np, addr); + + snprintf(name, MII_BUS_ID_SIZE, "%s@%llx", np->name, + (unsigned long long)taddr); +} + +/** + * hns_mdio_probe - probe mdio device + * @pdev: mdio platform device + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_probe(struct platform_device *pdev) +{ + struct device_node *np; + struct hns_mdio_device *mdio_dev; + struct mii_bus *new_bus; + struct resource *res; + int ret; + + if (!pdev) { + dev_err(NULL, "pdev is NULL!\r\n"); + return -ENODEV; + } + np = pdev->dev.of_node; + mdio_dev = devm_kzalloc(&pdev->dev, sizeof(*mdio_dev), GFP_KERNEL); + if (!mdio_dev) + return -ENOMEM; + + new_bus = devm_mdiobus_alloc(&pdev->dev); + if (!new_bus) { + dev_err(&pdev->dev, "mdiobus_alloc fail!\n"); + return -ENOMEM; + } + + new_bus->name = MDIO_BUS_NAME; + new_bus->read = hns_mdio_read; + new_bus->write = hns_mdio_write; + new_bus->reset = hns_mdio_reset; + new_bus->priv = mdio_dev; + hns_mdio_bus_name(new_bus->id, np); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mdio_dev->vbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mdio_dev->vbase)) { + ret = PTR_ERR(mdio_dev->vbase); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + mdio_dev->sys_vbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mdio_dev->sys_vbase)) { + ret = PTR_ERR(mdio_dev->sys_vbase); + return ret; + } + + new_bus->irq = devm_kcalloc(&pdev->dev, PHY_MAX_ADDR, + sizeof(int), GFP_KERNEL); + if (!new_bus->irq) + return -ENOMEM; + + new_bus->parent = &pdev->dev; + platform_set_drvdata(pdev, new_bus); + + ret = of_mdiobus_register(new_bus, np); + if (ret) { + dev_err(&pdev->dev, "Cannot register as MDIO bus!\n"); + platform_set_drvdata(pdev, NULL); + return ret; + } + + return 0; +} + +/** + * hns_mdio_remove - remove mdio device + * @pdev: mdio platform device + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_remove(struct platform_device *pdev) +{ + struct mii_bus *bus; + + bus = platform_get_drvdata(pdev); + + mdiobus_unregister(bus); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id hns_mdio_match[] = { + {.compatible = "hisilicon,mdio"}, + {.compatible = "hisilicon,hns-mdio"}, + {} +}; + +static struct platform_driver hns_mdio_driver = { + .probe = hns_mdio_probe, + .remove = hns_mdio_remove, + .driver = { + .name = MDIO_DRV_NAME, + .of_match_table = hns_mdio_match, + }, +}; + +module_platform_driver(hns_mdio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_DESCRIPTION("Hisilicon HNS MDIO driver"); +MODULE_ALIAS("platform:" MDIO_DRV_NAME); diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h index 28df37420da9..93ae11494810 100644 --- a/drivers/net/ethernet/ibm/emac/core.h +++ b/drivers/net/ethernet/ibm/emac/core.h @@ -181,7 +181,7 @@ struct emac_instance { struct mal_commac commac; /* PHY infos */ - u32 phy_mode; + int phy_mode; u32 phy_map; u32 phy_address; u32 phy_feat_exc; @@ -460,8 +460,8 @@ struct emac_ethtool_regs_subhdr { u32 index; }; -#define EMAC_ETHTOOL_REGS_VER 0 -#define EMAC4_ETHTOOL_REGS_VER 1 -#define EMAC4SYNC_ETHTOOL_REGS_VER 2 +#define EMAC_ETHTOOL_REGS_VER 3 +#define EMAC4_ETHTOOL_REGS_VER 4 +#define EMAC4SYNC_ETHTOOL_REGS_VER 5 #endif /* __IBM_NEWEMAC_CORE_H */ diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c index 45c8c864104e..b1af0d613caa 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_hw.c +++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c @@ -3900,10 +3900,6 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, return E1000_SUCCESS; } - /* If eeprom is not yet detected, do so now */ - if (eeprom->word_size == 0) - e1000_init_eeprom_params(hw); - /* A check for invalid values: offset too large, too many words, and * not enough words. */ @@ -4074,10 +4070,6 @@ static s32 e1000_do_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, return E1000_SUCCESS; } - /* If eeprom is not yet detected, do so now */ - if (eeprom->word_size == 0) - e1000_init_eeprom_params(hw); - /* A check for invalid values: offset too large, too many words, and * not enough words. */ diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index faf4b3f3d0b5..2e2ddec04a50 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -6952,6 +6952,7 @@ static const struct net_device_ops e1000e_netdev_ops = { #endif .ndo_set_features = e1000_set_features, .ndo_fix_features = e1000_fix_features, + .ndo_features_check = passthru_features_check, }; /** diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index c8c8c5baefda..14440200499b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -101,12 +101,19 @@ struct fm10k_tx_queue_stats { u64 csum_err; u64 tx_busy; u64 tx_done_old; + u64 csum_good; }; struct fm10k_rx_queue_stats { u64 alloc_failed; u64 csum_err; u64 errors; + u64 csum_good; + u64 switch_errors; + u64 drops; + u64 pp_errors; + u64 link_errors; + u64 length_errors; }; struct fm10k_ring { @@ -251,6 +258,7 @@ struct fm10k_intfc { #define FM10K_FLAG_RSS_FIELD_IPV6_UDP (u32)(1 << 2) #define FM10K_FLAG_RX_TS_ENABLED (u32)(1 << 3) #define FM10K_FLAG_SWPRI_CONFIG (u32)(1 << 4) +#define FM10K_FLAG_DEBUG_STATS (u32)(1 << 5) int xcast_mode; /* Tx fast path data */ @@ -277,6 +285,17 @@ struct fm10k_intfc { u64 rx_drops_nic; u64 rx_overrun_pf; u64 rx_overrun_vf; + + /* Debug Statistics */ + u64 hw_sm_mbx_full; + u64 hw_csum_tx_good; + u64 hw_csum_rx_good; + u64 rx_switch_errors; + u64 rx_drops; + u64 rx_pp_errors; + u64 rx_link_errors; + u64 rx_length_errors; + u32 tx_timeout_count; /* RX */ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index c6dc9683429e..4ef2fbd22911 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -76,19 +76,22 @@ static const struct fm10k_stats fm10k_gstrings_global_stats[] = { FM10K_STAT("mac_rules_used", hw.swapi.mac.used), FM10K_STAT("mac_rules_avail", hw.swapi.mac.avail), - FM10K_STAT("mbx_tx_busy", hw.mbx.tx_busy), - FM10K_STAT("mbx_tx_oversized", hw.mbx.tx_dropped), - FM10K_STAT("mbx_tx_messages", hw.mbx.tx_messages), - FM10K_STAT("mbx_tx_dwords", hw.mbx.tx_dwords), - FM10K_STAT("mbx_rx_messages", hw.mbx.rx_messages), - FM10K_STAT("mbx_rx_dwords", hw.mbx.rx_dwords), - FM10K_STAT("mbx_rx_parse_err", hw.mbx.rx_parse_err), - FM10K_STAT("tx_hang_count", tx_timeout_count), FM10K_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts), }; +static const struct fm10k_stats fm10k_gstrings_debug_stats[] = { + FM10K_STAT("hw_sm_mbx_full", hw_sm_mbx_full), + FM10K_STAT("hw_csum_tx_good", hw_csum_tx_good), + FM10K_STAT("hw_csum_rx_good", hw_csum_rx_good), + FM10K_STAT("rx_switch_errors", rx_switch_errors), + FM10K_STAT("rx_drops", rx_drops), + FM10K_STAT("rx_pp_errors", rx_pp_errors), + FM10K_STAT("rx_link_errors", rx_link_errors), + FM10K_STAT("rx_length_errors", rx_length_errors), +}; + static const struct fm10k_stats fm10k_gstrings_pf_stats[] = { FM10K_STAT("timeout", stats.timeout.count), FM10K_STAT("ur", stats.ur.count), @@ -100,14 +103,33 @@ static const struct fm10k_stats fm10k_gstrings_pf_stats[] = { FM10K_STAT("nodesc_drop", stats.nodesc_drop.count), }; +#define FM10K_MBX_STAT(_name, _stat) { \ + .stat_string = _name, \ + .sizeof_stat = FIELD_SIZEOF(struct fm10k_mbx_info, _stat), \ + .stat_offset = offsetof(struct fm10k_mbx_info, _stat) \ +} + +static const struct fm10k_stats fm10k_gstrings_mbx_stats[] = { + FM10K_MBX_STAT("mbx_tx_busy", tx_busy), + FM10K_MBX_STAT("mbx_tx_oversized", tx_dropped), + FM10K_MBX_STAT("mbx_tx_messages", tx_messages), + FM10K_MBX_STAT("mbx_tx_dwords", tx_dwords), + FM10K_MBX_STAT("mbx_rx_messages", rx_messages), + FM10K_MBX_STAT("mbx_rx_dwords", rx_dwords), + FM10K_MBX_STAT("mbx_rx_parse_err", rx_parse_err), +}; + #define FM10K_GLOBAL_STATS_LEN ARRAY_SIZE(fm10k_gstrings_global_stats) +#define FM10K_DEBUG_STATS_LEN ARRAY_SIZE(fm10k_gstrings_debug_stats) #define FM10K_PF_STATS_LEN ARRAY_SIZE(fm10k_gstrings_pf_stats) +#define FM10K_MBX_STATS_LEN ARRAY_SIZE(fm10k_gstrings_mbx_stats) #define FM10K_QUEUE_STATS_LEN(_n) \ ( (_n) * 2 * (sizeof(struct fm10k_queue_stats) / sizeof(u64))) #define FM10K_STATIC_STATS_LEN (FM10K_GLOBAL_STATS_LEN + \ - FM10K_NETDEV_STATS_LEN) + FM10K_NETDEV_STATS_LEN + \ + FM10K_MBX_STATS_LEN) static const char fm10k_gstrings_test[][ETH_GSTRING_LEN] = { "Mailbox test (on/offline)" @@ -120,47 +142,97 @@ enum fm10k_self_test_types { FM10K_TEST_MAX = FM10K_TEST_LEN }; -static void fm10k_get_strings(struct net_device *dev, u32 stringset, u8 *data) +enum { + FM10K_PRV_FLAG_DEBUG_STATS, + FM10K_PRV_FLAG_LEN, +}; + +static const char fm10k_prv_flags[FM10K_PRV_FLAG_LEN][ETH_GSTRING_LEN] = { + "debug-statistics", +}; + +static void fm10k_get_stat_strings(struct net_device *dev, u8 *data) { struct fm10k_intfc *interface = netdev_priv(dev); + struct fm10k_iov_data *iov_data = interface->iov_data; char *p = (char *)data; unsigned int i; + unsigned int j; - switch (stringset) { - case ETH_SS_TEST: - memcpy(data, *fm10k_gstrings_test, - FM10K_TEST_LEN * ETH_GSTRING_LEN); - break; - case ETH_SS_STATS: - for (i = 0; i < FM10K_NETDEV_STATS_LEN; i++) { - memcpy(p, fm10k_gstrings_net_stats[i].stat_string, + for (i = 0; i < FM10K_NETDEV_STATS_LEN; i++) { + memcpy(p, fm10k_gstrings_net_stats[i].stat_string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + + for (i = 0; i < FM10K_GLOBAL_STATS_LEN; i++) { + memcpy(p, fm10k_gstrings_global_stats[i].stat_string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + + if (interface->flags & FM10K_FLAG_DEBUG_STATS) { + for (i = 0; i < FM10K_DEBUG_STATS_LEN; i++) { + memcpy(p, fm10k_gstrings_debug_stats[i].stat_string, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } - for (i = 0; i < FM10K_GLOBAL_STATS_LEN; i++) { - memcpy(p, fm10k_gstrings_global_stats[i].stat_string, + } + + for (i = 0; i < FM10K_MBX_STATS_LEN; i++) { + memcpy(p, fm10k_gstrings_mbx_stats[i].stat_string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + + if (interface->hw.mac.type != fm10k_mac_vf) { + for (i = 0; i < FM10K_PF_STATS_LEN; i++) { + memcpy(p, fm10k_gstrings_pf_stats[i].stat_string, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } + } - if (interface->hw.mac.type != fm10k_mac_vf) { - for (i = 0; i < FM10K_PF_STATS_LEN; i++) { - memcpy(p, fm10k_gstrings_pf_stats[i].stat_string, - ETH_GSTRING_LEN); + if ((interface->flags & FM10K_FLAG_DEBUG_STATS) && iov_data) { + for (i = 0; i < iov_data->num_vfs; i++) { + for (j = 0; j < FM10K_MBX_STATS_LEN; j++) { + snprintf(p, + ETH_GSTRING_LEN, + "vf_%u_%s", i, + fm10k_gstrings_mbx_stats[j].stat_string); p += ETH_GSTRING_LEN; } } + } - for (i = 0; i < interface->hw.mac.max_queues; i++) { - sprintf(p, "tx_queue_%u_packets", i); - p += ETH_GSTRING_LEN; - sprintf(p, "tx_queue_%u_bytes", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rx_queue_%u_packets", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rx_queue_%u_bytes", i); - p += ETH_GSTRING_LEN; - } + for (i = 0; i < interface->hw.mac.max_queues; i++) { + sprintf(p, "tx_queue_%u_packets", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_queue_%u_bytes", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rx_queue_%u_packets", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rx_queue_%u_bytes", i); + p += ETH_GSTRING_LEN; + } +} + +static void fm10k_get_strings(struct net_device *dev, + u32 stringset, u8 *data) +{ + char *p = (char *)data; + + switch (stringset) { + case ETH_SS_TEST: + memcpy(data, *fm10k_gstrings_test, + FM10K_TEST_LEN * ETH_GSTRING_LEN); + break; + case ETH_SS_STATS: + fm10k_get_stat_strings(dev, data); + break; + case ETH_SS_PRIV_FLAGS: + memcpy(p, fm10k_prv_flags, + FM10K_PRV_FLAG_LEN * ETH_GSTRING_LEN); break; } } @@ -168,6 +240,7 @@ static void fm10k_get_strings(struct net_device *dev, u32 stringset, u8 *data) static int fm10k_get_sset_count(struct net_device *dev, int sset) { struct fm10k_intfc *interface = netdev_priv(dev); + struct fm10k_iov_data *iov_data = interface->iov_data; struct fm10k_hw *hw = &interface->hw; int stats_len = FM10K_STATIC_STATS_LEN; @@ -180,7 +253,16 @@ static int fm10k_get_sset_count(struct net_device *dev, int sset) if (hw->mac.type != fm10k_mac_vf) stats_len += FM10K_PF_STATS_LEN; + if (interface->flags & FM10K_FLAG_DEBUG_STATS) { + stats_len += FM10K_DEBUG_STATS_LEN; + + if (iov_data) + stats_len += FM10K_MBX_STATS_LEN * iov_data->num_vfs; + } + return stats_len; + case ETH_SS_PRIV_FLAGS: + return FM10K_PRV_FLAG_LEN; default: return -EOPNOTSUPP; } @@ -192,6 +274,7 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev, { const int stat_count = sizeof(struct fm10k_queue_stats) / sizeof(u64); struct fm10k_intfc *interface = netdev_priv(netdev); + struct fm10k_iov_data *iov_data = interface->iov_data; struct net_device_stats *net_stats = &netdev->stats; char *p; int i, j; @@ -211,13 +294,47 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev, sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } - if (interface->hw.mac.type != fm10k_mac_vf) + if (interface->flags & FM10K_FLAG_DEBUG_STATS) { + for (i = 0; i < FM10K_DEBUG_STATS_LEN; i++) { + p = (char *)interface + fm10k_gstrings_debug_stats[i].stat_offset; + *(data++) = (fm10k_gstrings_debug_stats[i].sizeof_stat == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } + } + + for (i = 0; i < FM10K_MBX_STATS_LEN; i++) { + p = (char *)&interface->hw.mbx + fm10k_gstrings_mbx_stats[i].stat_offset; + *(data++) = (fm10k_gstrings_mbx_stats[i].sizeof_stat == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } + + if (interface->hw.mac.type != fm10k_mac_vf) { for (i = 0; i < FM10K_PF_STATS_LEN; i++) { p = (char *)interface + fm10k_gstrings_pf_stats[i].stat_offset; *(data++) = (fm10k_gstrings_pf_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } + } + + if ((interface->flags & FM10K_FLAG_DEBUG_STATS) && iov_data) { + for (i = 0; i < iov_data->num_vfs; i++) { + struct fm10k_vf_info *vf_info; + vf_info = &iov_data->vf_info[i]; + + /* skip stats if we don't have a vf info */ + if (!vf_info) { + data += FM10K_MBX_STATS_LEN; + continue; + } + + for (j = 0; j < FM10K_MBX_STATS_LEN; j++) { + p = (char *)&vf_info->mbx + fm10k_gstrings_mbx_stats[j].stat_offset; + *(data++) = (fm10k_gstrings_mbx_stats[j].sizeof_stat == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } + } + } for (i = 0; i < interface->hw.mac.max_queues; i++) { struct fm10k_ring *ring; @@ -881,6 +998,33 @@ static void fm10k_self_test(struct net_device *dev, eth_test->flags |= ETH_TEST_FL_FAILED; } +static u32 fm10k_get_priv_flags(struct net_device *netdev) +{ + struct fm10k_intfc *interface = netdev_priv(netdev); + u32 priv_flags = 0; + + if (interface->flags & FM10K_FLAG_DEBUG_STATS) + priv_flags |= 1 << FM10K_PRV_FLAG_DEBUG_STATS; + + return priv_flags; +} + +static int fm10k_set_priv_flags(struct net_device *netdev, u32 priv_flags) +{ + struct fm10k_intfc *interface = netdev_priv(netdev); + + if (priv_flags >= (1 << FM10K_PRV_FLAG_LEN)) + return -EINVAL; + + if (priv_flags & (1 << FM10K_PRV_FLAG_DEBUG_STATS)) + interface->flags |= FM10K_FLAG_DEBUG_STATS; + else + interface->flags &= ~FM10K_FLAG_DEBUG_STATS; + + return 0; +} + + static u32 fm10k_get_reta_size(struct net_device __always_unused *netdev) { return FM10K_RETA_SIZE * FM10K_RETA_ENTRIES_PER_REG; @@ -1094,6 +1238,8 @@ static const struct ethtool_ops fm10k_ethtool_ops = { .get_regs = fm10k_get_regs, .get_regs_len = fm10k_get_regs_len, .self_test = fm10k_self_test, + .get_priv_flags = fm10k_get_priv_flags, + .set_priv_flags = fm10k_set_priv_flags, .get_rxfh_indir_size = fm10k_get_reta_size, .get_rxfh_key_size = fm10k_get_rssrk_size, .get_rxfh = fm10k_get_rssh, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_iov.c b/drivers/net/ethernet/intel/fm10k/fm10k_iov.c index 0e25a80417b9..acfb8b1f88a7 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_iov.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_iov.c @@ -137,8 +137,11 @@ process_mbx: } /* guarantee we have free space in the SM mailbox */ - if (!hw->mbx.ops.tx_ready(&hw->mbx, FM10K_VFMBX_MSG_MTU)) + if (!hw->mbx.ops.tx_ready(&hw->mbx, FM10K_VFMBX_MSG_MTU)) { + /* keep track of how many times this occurs */ + interface->hw_sm_mbx_full++; break; + } /* cleanup mailbox and process received messages */ mbx->ops.process(hw, mbx); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index 92d415584749..2f47bfe6cc90 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -398,6 +398,8 @@ static inline void fm10k_rx_checksum(struct fm10k_ring *ring, return; skb->ip_summed = CHECKSUM_UNNECESSARY; + + ring->rx_stats.csum_good++; } #define FM10K_RSS_L4_TYPES_MASK \ @@ -556,6 +558,18 @@ static bool fm10k_cleanup_headers(struct fm10k_ring *rx_ring, { if (unlikely((fm10k_test_staterr(rx_desc, FM10K_RXD_STATUS_RXE)))) { +#define FM10K_TEST_RXD_BIT(rxd, bit) \ + ((rxd)->w.csum_err & cpu_to_le16(bit)) + if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_SWITCH_ERROR)) + rx_ring->rx_stats.switch_errors++; + if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_NO_DESCRIPTOR)) + rx_ring->rx_stats.drops++; + if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_PP_ERROR)) + rx_ring->rx_stats.pp_errors++; + if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_SWITCH_READY)) + rx_ring->rx_stats.link_errors++; + if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_TOO_BIG)) + rx_ring->rx_stats.length_errors++; dev_kfree_skb_any(skb); rx_ring->rx_stats.errors++; return true; @@ -881,6 +895,7 @@ static void fm10k_tx_csum(struct fm10k_ring *tx_ring, /* update TX checksum flag */ first->tx_flags |= FM10K_TX_FLAGS_CSUM; + tx_ring->tx_stats.csum_good++; no_csum: /* populate Tx descriptor header size and mss */ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c index 1a4b52637de9..af09a1b272e6 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c @@ -129,8 +129,8 @@ static u16 fm10k_fifo_head_drop(struct fm10k_mbx_fifo *fifo) * fm10k_fifo_drop_all - Drop all messages in FIFO * @fifo: pointer to FIFO * - * This function resets the head pointer to drop all messages in the FIFO, - * and ensure the FIFO is empty. + * This function resets the head pointer to drop all messages in the FIFO and + * ensure the FIFO is empty. **/ static void fm10k_fifo_drop_all(struct fm10k_mbx_fifo *fifo) { @@ -899,6 +899,27 @@ static void fm10k_mbx_create_disconnect_hdr(struct fm10k_mbx_info *mbx) } /** + * fm10k_mbx_create_fake_disconnect_hdr - Generate a false disconnect mailbox header + * @mbx: pointer to mailbox + * + * This function creates a fake disconnect header for loading into remote + * mailbox header. The primary purpose is to prevent errors on immediate + * start up after mbx->connect. + **/ +static void fm10k_mbx_create_fake_disconnect_hdr(struct fm10k_mbx_info *mbx) +{ + u32 hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_DISCONNECT, TYPE) | + FM10K_MSG_HDR_FIELD_SET(mbx->head, TAIL) | + FM10K_MSG_HDR_FIELD_SET(mbx->tail, HEAD); + u16 crc = fm10k_crc_16b(&hdr, mbx->local, 1); + + mbx->mbx_lock |= FM10K_MBX_ACK; + + /* load header to memory to be written */ + mbx->mbx_hdr = hdr | FM10K_MSG_HDR_FIELD_SET(crc, CRC); +} + +/** * fm10k_mbx_create_error_msg - Generate a error message * @mbx: pointer to mailbox * @err: local error encountered @@ -1046,9 +1067,26 @@ static s32 fm10k_mbx_create_reply(struct fm10k_hw *hw, **/ static void fm10k_mbx_reset_work(struct fm10k_mbx_info *mbx) { + u16 len, head, ack; + /* reset our outgoing max size back to Rx limits */ mbx->max_size = mbx->rx.size - 1; + /* update mbx->pulled to account for tail_len and ack */ + head = FM10K_MSG_HDR_FIELD_GET(mbx->mbx_hdr, HEAD); + ack = fm10k_mbx_index_len(mbx, head, mbx->tail); + mbx->pulled += mbx->tail_len - ack; + + /* now drop any messages which have started or finished transmitting */ + while (fm10k_fifo_head_len(&mbx->tx) && mbx->pulled) { + len = fm10k_fifo_head_drop(&mbx->tx); + mbx->tx_dropped++; + if (mbx->pulled >= len) + mbx->pulled -= len; + else + mbx->pulled = 0; + } + /* just do a quick resysnc to start of message */ mbx->pushed = 0; mbx->pulled = 0; @@ -1418,8 +1456,10 @@ static s32 fm10k_mbx_connect(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx) /* Place mbx in ready to connect state */ mbx->state = FM10K_STATE_CONNECT; + fm10k_mbx_reset_work(mbx); + /* initialize header of remote mailbox */ - fm10k_mbx_create_disconnect_hdr(mbx); + fm10k_mbx_create_fake_disconnect_hdr(mbx); fm10k_write_reg(hw, mbx->mbmem_reg ^ mbx->mbmem_len, mbx->mbx_hdr); /* enable interrupt and notify other party of new message */ @@ -1725,7 +1765,7 @@ static void fm10k_sm_mbx_disconnect(struct fm10k_hw *hw, mbx->state = FM10K_STATE_CLOSED; mbx->remote = 0; fm10k_mbx_reset_work(mbx); - fm10k_mbx_update_max_size(mbx, 0); + fm10k_fifo_drop_all(&mbx->tx); fm10k_write_reg(hw, mbx->mbmem_reg, 0); } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 3d71c5206110..74be792f3f1b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -274,8 +274,6 @@ static void fm10k_watchdog_update_host_state(struct fm10k_intfc *interface) * @interface: board private structure * * This function will process both the upstream and downstream mailboxes. - * It is necessary for us to hold the rtnl_lock while doing this as the - * mailbox accesses are protected by this lock. **/ static void fm10k_mbx_subtask(struct fm10k_intfc *interface) { @@ -330,6 +328,9 @@ void fm10k_update_stats(struct fm10k_intfc *interface) { struct net_device_stats *net_stats = &interface->netdev->stats; struct fm10k_hw *hw = &interface->hw; + u64 hw_csum_tx_good = 0, hw_csum_rx_good = 0, rx_length_errors = 0; + u64 rx_switch_errors = 0, rx_drops = 0, rx_pp_errors = 0; + u64 rx_link_errors = 0; u64 rx_errors = 0, rx_csum_errors = 0, tx_csum_errors = 0; u64 restart_queue = 0, tx_busy = 0, alloc_failed = 0; u64 rx_bytes_nic = 0, rx_pkts_nic = 0, rx_drops_nic = 0; @@ -349,6 +350,7 @@ void fm10k_update_stats(struct fm10k_intfc *interface) tx_csum_errors += tx_ring->tx_stats.csum_err; bytes += tx_ring->stats.bytes; pkts += tx_ring->stats.packets; + hw_csum_tx_good += tx_ring->tx_stats.csum_good; } interface->restart_queue = restart_queue; @@ -356,6 +358,8 @@ void fm10k_update_stats(struct fm10k_intfc *interface) net_stats->tx_bytes = bytes; net_stats->tx_packets = pkts; interface->tx_csum_errors = tx_csum_errors; + interface->hw_csum_tx_good = hw_csum_tx_good; + /* gather some stats to the interface struct that are per queue */ for (bytes = 0, pkts = 0, i = 0; i < interface->num_rx_queues; i++) { struct fm10k_ring *rx_ring = interface->rx_ring[i]; @@ -365,12 +369,24 @@ void fm10k_update_stats(struct fm10k_intfc *interface) alloc_failed += rx_ring->rx_stats.alloc_failed; rx_csum_errors += rx_ring->rx_stats.csum_err; rx_errors += rx_ring->rx_stats.errors; + hw_csum_rx_good += rx_ring->rx_stats.csum_good; + rx_switch_errors += rx_ring->rx_stats.switch_errors; + rx_drops += rx_ring->rx_stats.drops; + rx_pp_errors += rx_ring->rx_stats.pp_errors; + rx_link_errors += rx_ring->rx_stats.link_errors; + rx_length_errors += rx_ring->rx_stats.length_errors; } net_stats->rx_bytes = bytes; net_stats->rx_packets = pkts; interface->alloc_failed = alloc_failed; interface->rx_csum_errors = rx_csum_errors; + interface->hw_csum_rx_good = hw_csum_rx_good; + interface->rx_switch_errors = rx_switch_errors; + interface->rx_drops = rx_drops; + interface->rx_pp_errors = rx_pp_errors; + interface->rx_link_errors = rx_link_errors; + interface->rx_length_errors = rx_length_errors; hw->mac.ops.update_hw_stats(hw, &interface->stats); @@ -498,7 +514,7 @@ static void fm10k_service_task(struct work_struct *work) interface = container_of(work, struct fm10k_intfc, service_task); - /* tasks always capable of running, but must be rtnl protected */ + /* tasks run even when interface is down */ fm10k_mbx_subtask(interface); fm10k_detach_subtask(interface); fm10k_reset_subtask(interface); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h index bac8d486d75f..318a212f0a78 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h @@ -762,6 +762,12 @@ enum fm10k_rxdesc_xc { #define FM10K_RXD_STATUS_L4E 0x4000 /* L4 csum error */ #define FM10K_RXD_STATUS_IPE 0x8000 /* IPv4 csum error */ +#define FM10K_RXD_ERR_SWITCH_ERROR 0x0001 /* Switch found bad packet */ +#define FM10K_RXD_ERR_NO_DESCRIPTOR 0x0002 /* No descriptor available */ +#define FM10K_RXD_ERR_PP_ERROR 0x0004 /* RAM error during processing */ +#define FM10K_RXD_ERR_SWITCH_READY 0x0008 /* Link transition mid-packet */ +#define FM10K_RXD_ERR_TOO_BIG 0x0010 /* Pkt too big for single buf */ + struct fm10k_ftag { __be16 swpri_type_user; __be16 vlan; diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index e7462793d48d..f26dcb23ebf9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -71,7 +71,6 @@ #define I40E_MAX_VEB 16 #define I40E_MAX_NUM_DESCRIPTORS 4096 -#define I40E_MAX_REGISTER 0x800000 #define I40E_MAX_CSR_SPACE (4 * 1024 * 1024 - 64 * 1024) #define I40E_DEFAULT_NUM_DESCRIPTORS 512 #define I40E_REQ_DESCRIPTOR_MULTIPLE 32 @@ -98,10 +97,11 @@ #define I40E_MAX_USER_PRIORITY 8 #define I40E_DEFAULT_MSG_ENABLE 4 #define I40E_QUEUE_WAIT_RETRY_LIMIT 10 -#define I40E_INT_NAME_STR_LEN (IFNAMSIZ + 9) +#define I40E_INT_NAME_STR_LEN (IFNAMSIZ + 16) /* Ethtool Private Flags */ #define I40E_PRIV_FLAGS_NPAR_FLAG BIT(0) +#define I40E_PRIV_FLAGS_LINKPOLL_FLAG BIT(1) #define I40E_NVM_VERSION_LO_SHIFT 0 #define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT) @@ -243,7 +243,6 @@ struct i40e_pf { struct pci_dev *pdev; struct i40e_hw hw; unsigned long state; - unsigned long link_check_timeout; struct msix_entry *msix_entries; bool fc_autoneg_status; @@ -327,7 +326,9 @@ struct i40e_pf { #define I40E_FLAG_OUTER_UDP_CSUM_CAPABLE BIT_ULL(33) #define I40E_FLAG_128_QP_RSS_CAPABLE BIT_ULL(34) #define I40E_FLAG_WB_ON_ITR_CAPABLE BIT_ULL(35) +#define I40E_FLAG_VEB_STATS_ENABLED BIT_ULL(37) #define I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(38) +#define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(39) #define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40) /* tracks features that get auto disabled by errors */ @@ -409,6 +410,9 @@ struct i40e_pf { /* These are only valid in NPAR modes */ u32 npar_max_bw; u32 npar_min_bw; + + u32 ioremap_len; + u32 fd_inv; }; struct i40e_mac_filter { @@ -474,6 +478,7 @@ struct i40e_vsi { #endif u32 tx_restart; u32 tx_busy; + u64 tx_linearize; u32 rx_buf_failed; u32 rx_page_failed; @@ -534,6 +539,7 @@ struct i40e_vsi { u16 idx; /* index in pf->vsi[] */ u16 veb_idx; /* index of VEB parent */ struct kobject *kobj; /* sysfs object */ + bool current_isup; /* Sync 'link up' logging */ /* VSI specific handlers */ irqreturn_t (*irq_handler)(int irq, void *data); @@ -667,7 +673,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, bool is_vf, bool is_netdev); void i40e_del_filter(struct i40e_vsi *vsi, u8 *macaddr, s16 vlan, bool is_vf, bool is_netdev); -int i40e_sync_vsi_filters(struct i40e_vsi *vsi); +int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl); struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, u16 uplink, u32 param1); int i40e_vsi_release(struct i40e_vsi *vsi); @@ -700,7 +706,24 @@ static inline void i40e_dbg_pf_exit(struct i40e_pf *pf) {} static inline void i40e_dbg_init(void) {} static inline void i40e_dbg_exit(void) {} #endif /* CONFIG_DEBUG_FS*/ -void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector); +/** + * i40e_irq_dynamic_enable - Enable default interrupt generation settings + * @vsi: pointer to a vsi + * @vector: enable a particular Hw Interrupt vector, without base_vector + **/ +static inline void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector) +{ + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + u32 val; + + val = I40E_PFINT_DYN_CTLN_INTENA_MASK | + I40E_PFINT_DYN_CTLN_CLEARPBA_MASK | + (I40E_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT); + wr32(hw, I40E_PFINT_DYN_CTLN(vector + vsi->base_vector - 1), val); + /* skip the flush */ +} + void i40e_irq_dynamic_disable(struct i40e_vsi *vsi, int vector); void i40e_irq_dynamic_disable_icr0(struct i40e_pf *pf); void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf); @@ -739,7 +762,7 @@ int i40e_fcoe_vsi_init(struct i40e_vsi *vsi, struct i40e_vsi_context *ctxt); u8 i40e_get_fcoe_tc_map(struct i40e_pf *pf); void i40e_fcoe_config_netdev(struct net_device *netdev, struct i40e_vsi *vsi); void i40e_fcoe_vsi_setup(struct i40e_pf *pf); -int i40e_init_pf_fcoe(struct i40e_pf *pf); +void i40e_init_pf_fcoe(struct i40e_pf *pf); int i40e_fcoe_setup_ddp_resources(struct i40e_vsi *vsi); void i40e_fcoe_free_ddp_resources(struct i40e_vsi *vsi); int i40e_fcoe_handle_offload(struct i40e_ring *rx_ring, @@ -771,4 +794,5 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi); i40e_status i40e_get_npar_bw_setting(struct i40e_pf *pf); i40e_status i40e_set_npar_bw_setting(struct i40e_pf *pf); i40e_status i40e_commit_npar_bw_setting(struct i40e_pf *pf); +void i40e_print_link_message(struct i40e_vsi *vsi, bool isup); #endif /* _I40E_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index 3e0d20037675..fa2e916b23da 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -482,8 +482,12 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.asq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_asq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.asq.head, 0); @@ -492,16 +496,13 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) wr32(hw, hw->aq.asq.bal, 0); wr32(hw, hw->aq.asq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.asq_mutex); - hw->aq.asq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_asq_bufs(hw); +shutdown_asq_out: mutex_unlock(&hw->aq.asq_mutex); - return ret_code; } @@ -515,8 +516,12 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.arq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.arq_mutex); + + if (hw->aq.arq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_arq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.arq.head, 0); @@ -525,16 +530,13 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) wr32(hw, hw->aq.arq.bal, 0); wr32(hw, hw->aq.arq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.arq_mutex); - hw->aq.arq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_arq_bufs(hw); +shutdown_arq_out: mutex_unlock(&hw->aq.arq_mutex); - return ret_code; } @@ -657,6 +659,9 @@ i40e_status i40e_shutdown_adminq(struct i40e_hw *hw) /* destroy the locks */ + if (hw->nvm_buff.va) + i40e_free_virt_mem(hw, &hw->nvm_buff); + return ret_code; } @@ -678,8 +683,7 @@ static u16 i40e_clean_asq(struct i40e_hw *hw) details = I40E_ADMINQ_DETAILS(*asq, ntc); while (rd32(hw, hw->aq.asq.head) != ntc) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "%s: ntc %d head %d.\n", __func__, ntc, - rd32(hw, hw->aq.asq.head)); + "ntc %d head %d.\n", ntc, rd32(hw, hw->aq.asq.head)); if (details->callback) { I40E_ADMINQ_CALLBACK cb_func = @@ -742,19 +746,23 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, u16 retval = 0; u32 val = 0; - val = rd32(hw, hw->aq.asq.head); - if (val >= hw->aq.num_asq_entries) { + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: head overrun at %d\n", val); + "AQTX: Admin queue not initialized.\n"); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } - if (hw->aq.asq.count == 0) { + hw->aq.asq_last_status = I40E_AQ_RC_OK; + + val = rd32(hw, hw->aq.asq.head); + if (val >= hw->aq.num_asq_entries) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: Admin queue not initialized.\n"); + "AQTX: head overrun at %d\n", val); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use); @@ -779,8 +787,6 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, desc->flags &= ~cpu_to_le16(details->flags_dis); desc->flags |= cpu_to_le16(details->flags_ena); - mutex_lock(&hw->aq.asq_mutex); - if (buff_size > hw->aq.asq_buf_size) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, @@ -889,6 +895,10 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, "AQTX: desc and buffer writeback:\n"); i40e_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, buff, buff_size); + /* save writeback aq if requested */ + if (details->wb_desc) + *details->wb_desc = *desc_on_ring; + /* update the error if time out occurred */ if ((!cmd_completed) && (!details->async && !details->postpone)) { @@ -900,7 +910,6 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, asq_send_command_error: mutex_unlock(&hw->aq.asq_mutex); -asq_send_command_exit: return status; } @@ -946,6 +955,13 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw, /* take the lock before we start messing with the ring */ mutex_lock(&hw->aq.arq_mutex); + if (hw->aq.arq.count == 0) { + i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, + "AQRX: Admin queue not initialized.\n"); + ret_code = I40E_ERR_QUEUE_EMPTY; + goto clean_arq_element_err; + } + /* set next_to_use to head */ ntu = (rd32(hw, hw->aq.arq.head) & I40E_PF_ARQH_ARQH_MASK); if (ntu == ntc) { @@ -1007,6 +1023,8 @@ clean_arq_element_out: /* Set pending if needed, unlock and return */ if (pending != NULL) *pending = (ntc > ntu ? hw->aq.arq.count : 0) + (ntu - ntc); + +clean_arq_element_err: mutex_unlock(&hw->aq.arq_mutex); if (i40e_is_nvm_update_op(&e->desc)) { @@ -1014,6 +1032,19 @@ clean_arq_element_out: i40e_release_nvm(hw); hw->aq.nvm_release_on_done = false; } + + switch (hw->nvmupd_state) { + case I40E_NVMUPD_STATE_INIT_WAIT: + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + break; + + case I40E_NVMUPD_STATE_WRITE_WAIT: + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING; + break; + + default: + break; + } } return ret_code; diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.h b/drivers/net/ethernet/intel/i40e/i40e_adminq.h index 28e519a50de4..12fbbddea299 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.h @@ -69,6 +69,7 @@ struct i40e_asq_cmd_details { u16 flags_dis; bool async; bool postpone; + struct i40e_aq_desc *wb_desc; }; #define I40E_ADMINQ_DETAILS(R, i) \ @@ -108,9 +109,10 @@ struct i40e_adminq_info { /** * i40e_aq_rc_to_posix - convert errors to user-land codes - * aq_rc: AdminQ error code to convert + * aq_ret: AdminQ handler error code can override aq_rc + * aq_rc: AdminQ firmware error code to convert **/ -static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc) +static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) { int aq_to_posix[] = { 0, /* I40E_AQ_RC_OK */ @@ -142,8 +144,9 @@ static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc) if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT) return -EAGAIN; - if (aq_rc >= ARRAY_SIZE(aq_to_posix)) + if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0])))) return -ERANGE; + return aq_to_posix[aq_rc]; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 95d23bfbcbf1..785b3dbd22ca 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -2062,6 +2062,7 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start); #define I40E_AQC_CEE_APP_ISCSI_MASK (0x7 << I40E_AQC_CEE_APP_ISCSI_SHIFT) #define I40E_AQC_CEE_APP_FIP_SHIFT 0x8 #define I40E_AQC_CEE_APP_FIP_MASK (0x7 << I40E_AQC_CEE_APP_FIP_SHIFT) + #define I40E_AQC_CEE_PG_STATUS_SHIFT 0x0 #define I40E_AQC_CEE_PG_STATUS_MASK (0x7 << I40E_AQC_CEE_PG_STATUS_SHIFT) #define I40E_AQC_CEE_PFC_STATUS_SHIFT 0x3 @@ -2070,10 +2071,19 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start); #define I40E_AQC_CEE_APP_STATUS_MASK (0x7 << I40E_AQC_CEE_APP_STATUS_SHIFT) #define I40E_AQC_CEE_FCOE_STATUS_SHIFT 0x8 #define I40E_AQC_CEE_FCOE_STATUS_MASK (0x7 << I40E_AQC_CEE_FCOE_STATUS_SHIFT) -#define I40E_AQC_CEE_ISCSI_STATUS_SHIFT 0xA +#define I40E_AQC_CEE_ISCSI_STATUS_SHIFT 0xB #define I40E_AQC_CEE_ISCSI_STATUS_MASK (0x7 << I40E_AQC_CEE_ISCSI_STATUS_SHIFT) #define I40E_AQC_CEE_FIP_STATUS_SHIFT 0x10 #define I40E_AQC_CEE_FIP_STATUS_MASK (0x7 << I40E_AQC_CEE_FIP_STATUS_SHIFT) + +/* struct i40e_aqc_get_cee_dcb_cfg_v1_resp was originally defined with + * word boundary layout issues, which the Linux compilers silently deal + * with by adding padding, making the actual struct larger than designed. + * However, the FW compiler for the NIC is less lenient and complains + * about the struct. Hence, the struct defined here has an extra byte in + * fields reserved3 and reserved4 to directly acknowledge that padding, + * and the new length is used in the length check macro. + */ struct i40e_aqc_get_cee_dcb_cfg_v1_resp { u8 reserved1; u8 oper_num_tc; @@ -2081,9 +2091,9 @@ struct i40e_aqc_get_cee_dcb_cfg_v1_resp { u8 reserved2; u8 oper_tc_bw[8]; u8 oper_pfc_en; - u8 reserved3; + u8 reserved3[2]; __le16 oper_app_prio; - u8 reserved4; + u8 reserved4[2]; __le16 tlv_status; }; diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 114dc6450183..2d012d9c22ad 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -51,7 +51,9 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw) case I40E_DEV_ID_QSFP_B: case I40E_DEV_ID_QSFP_C: case I40E_DEV_ID_10G_BASE_T: + case I40E_DEV_ID_10G_BASE_T4: case I40E_DEV_ID_20G_KR2: + case I40E_DEV_ID_20G_KR2_A: hw->mac.type = I40E_MAC_XL710; break; case I40E_DEV_ID_SFP_X722: @@ -441,9 +443,6 @@ static i40e_status i40e_aq_get_set_rss_lut(struct i40e_hw *hw, I40E_AQC_SET_RSS_LUT_TABLE_TYPE_SHIFT) & I40E_AQC_SET_RSS_LUT_TABLE_TYPE_MASK)); - cmd_resp->addr_high = cpu_to_le32(high_16_bits((u64)lut)); - cmd_resp->addr_low = cpu_to_le32(lower_32_bits((u64)lut)); - status = i40e_asq_send_command(hw, &desc, lut, lut_size, NULL); return status; @@ -518,8 +517,6 @@ static i40e_status i40e_aq_get_set_rss_key(struct i40e_hw *hw, I40E_AQC_SET_RSS_KEY_VSI_ID_SHIFT) & I40E_AQC_SET_RSS_KEY_VSI_ID_MASK)); cmd_resp->vsi_id |= cpu_to_le16((u16)I40E_AQC_SET_RSS_KEY_VSI_VALID); - cmd_resp->addr_high = cpu_to_le32(high_16_bits((u64)key)); - cmd_resp->addr_low = cpu_to_le32(lower_32_bits((u64)key)); status = i40e_asq_send_command(hw, &desc, key, key_size, NULL); @@ -1038,7 +1035,7 @@ i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr) status = i40e_aq_mac_address_read(hw, &flags, &addrs, NULL); if (flags & I40E_AQC_LAN_ADDR_VALID) - memcpy(mac_addr, &addrs.pf_lan_mac, sizeof(addrs.pf_lan_mac)); + ether_addr_copy(mac_addr, addrs.pf_lan_mac); return status; } @@ -1061,7 +1058,7 @@ i40e_status i40e_get_port_mac_addr(struct i40e_hw *hw, u8 *mac_addr) return status; if (flags & I40E_AQC_PORT_ADDR_VALID) - memcpy(mac_addr, &addrs.port_mac, sizeof(addrs.port_mac)); + ether_addr_copy(mac_addr, addrs.port_mac); else status = I40E_ERR_INVALID_MAC_ADDR; @@ -1119,7 +1116,7 @@ i40e_status i40e_get_san_mac_addr(struct i40e_hw *hw, u8 *mac_addr) return status; if (flags & I40E_AQC_SAN_ADDR_VALID) - memcpy(mac_addr, &addrs.pf_san_mac, sizeof(addrs.pf_san_mac)); + ether_addr_copy(mac_addr, addrs.pf_san_mac); else status = I40E_ERR_INVALID_MAC_ADDR; @@ -1260,7 +1257,7 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw) grst_del = (rd32(hw, I40E_GLGEN_RSTCTL) & I40E_GLGEN_RSTCTL_GRSTDEL_MASK) >> I40E_GLGEN_RSTCTL_GRSTDEL_SHIFT; - for (cnt = 0; cnt < grst_del + 2; cnt++) { + for (cnt = 0; cnt < grst_del + 10; cnt++) { reg = rd32(hw, I40E_GLGEN_RSTAT); if (!(reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK)) break; @@ -2238,27 +2235,28 @@ i40e_status i40e_aq_send_driver_version(struct i40e_hw *hw, /** * i40e_get_link_status - get status of the HW network link * @hw: pointer to the hw struct + * @link_up: pointer to bool (true/false = linkup/linkdown) * - * Returns true if link is up, false if link is down. + * Variable link_up true if link is up, false if link is down. + * The variable link_up is invalid if returned value of status != 0 * * Side effect: LinkStatusEvent reporting becomes enabled **/ -bool i40e_get_link_status(struct i40e_hw *hw) +i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up) { i40e_status status = 0; - bool link_status = false; if (hw->phy.get_link_info) { status = i40e_aq_get_link_info(hw, true, NULL, NULL); if (status) - goto i40e_get_link_status_exit; + i40e_debug(hw, I40E_DEBUG_LINK, "get link failed: status %d\n", + status); } - link_status = hw->phy.link_info.link_info & I40E_AQ_LINK_UP; + *link_up = hw->phy.link_info.link_info & I40E_AQ_LINK_UP; -i40e_get_link_status_exit: - return link_status; + return status; } /** @@ -2365,6 +2363,7 @@ i40e_status i40e_aq_get_veb_parameters(struct i40e_hw *hw, *vebs_free = le16_to_cpu(cmd_resp->vebs_free); if (floating) { u16 flags = le16_to_cpu(cmd_resp->veb_flags); + if (flags & I40E_AQC_ADD_VEB_FLOATING) *floating = true; else @@ -3779,7 +3778,7 @@ i40e_status i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw, } if (mac_addr) - memcpy(cmd->mac, mac_addr, ETH_ALEN); + ether_addr_copy(cmd->mac, mac_addr); cmd->etype = cpu_to_le16(ethtype); cmd->flags = cpu_to_le16(flags); diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c index 90de46aef557..6fa07ef1651d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c @@ -292,6 +292,182 @@ static void i40e_parse_ieee_tlv(struct i40e_lldp_org_tlv *tlv, } /** + * i40e_parse_cee_pgcfg_tlv + * @tlv: CEE DCBX PG CFG TLV + * @dcbcfg: Local store to update ETS CFG data + * + * Parses CEE DCBX PG CFG TLV + **/ +static void i40e_parse_cee_pgcfg_tlv(struct i40e_cee_feat_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + struct i40e_dcb_ets_config *etscfg; + u8 *buf = tlv->tlvinfo; + u16 offset = 0; + u8 priority; + int i; + + etscfg = &dcbcfg->etscfg; + + if (tlv->en_will_err & I40E_CEE_FEAT_TLV_WILLING_MASK) + etscfg->willing = 1; + + etscfg->cbs = 0; + /* Priority Group Table (4 octets) + * Octets:| 1 | 2 | 3 | 4 | + * ----------------------------------------- + * |pri0|pri1|pri2|pri3|pri4|pri5|pri6|pri7| + * ----------------------------------------- + * Bits:|7 4|3 0|7 4|3 0|7 4|3 0|7 4|3 0| + * ----------------------------------------- + */ + for (i = 0; i < 4; i++) { + priority = (u8)((buf[offset] & I40E_CEE_PGID_PRIO_1_MASK) >> + I40E_CEE_PGID_PRIO_1_SHIFT); + etscfg->prioritytable[i * 2] = priority; + priority = (u8)((buf[offset] & I40E_CEE_PGID_PRIO_0_MASK) >> + I40E_CEE_PGID_PRIO_0_SHIFT); + etscfg->prioritytable[i * 2 + 1] = priority; + offset++; + } + + /* PG Percentage Table (8 octets) + * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | + * --------------------------------- + * |pg0|pg1|pg2|pg3|pg4|pg5|pg6|pg7| + * --------------------------------- + */ + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + etscfg->tcbwtable[i] = buf[offset++]; + + /* Number of TCs supported (1 octet) */ + etscfg->maxtcs = buf[offset]; +} + +/** + * i40e_parse_cee_pfccfg_tlv + * @tlv: CEE DCBX PFC CFG TLV + * @dcbcfg: Local store to update PFC CFG data + * + * Parses CEE DCBX PFC CFG TLV + **/ +static void i40e_parse_cee_pfccfg_tlv(struct i40e_cee_feat_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u8 *buf = tlv->tlvinfo; + + if (tlv->en_will_err & I40E_CEE_FEAT_TLV_WILLING_MASK) + dcbcfg->pfc.willing = 1; + + /* ------------------------ + * | PFC Enable | PFC TCs | + * ------------------------ + * | 1 octet | 1 octet | + */ + dcbcfg->pfc.pfcenable = buf[0]; + dcbcfg->pfc.pfccap = buf[1]; +} + +/** + * i40e_parse_cee_app_tlv + * @tlv: CEE DCBX APP TLV + * @dcbcfg: Local store to update APP PRIO data + * + * Parses CEE DCBX APP PRIO TLV + **/ +static void i40e_parse_cee_app_tlv(struct i40e_cee_feat_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u16 length, typelength, offset = 0; + struct i40e_cee_app_prio *app; + u8 i, up; + + typelength = ntohs(tlv->hdr.typelen); + length = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >> + I40E_LLDP_TLV_LEN_SHIFT); + + dcbcfg->numapps = length / sizeof(*app); + if (!dcbcfg->numapps) + return; + + for (i = 0; i < dcbcfg->numapps; i++) { + app = (struct i40e_cee_app_prio *)(tlv->tlvinfo + offset); + for (up = 0; up < I40E_MAX_USER_PRIORITY; up++) { + if (app->prio_map & (1 << up)) + break; + } + dcbcfg->app[i].priority = up; + /* Get Selector from lower 2 bits */ + dcbcfg->app[i].selector = (app->upper_oui_sel & + I40E_CEE_APP_SELECTOR_MASK); + dcbcfg->app[i].protocolid = ntohs(app->protocol); + /* Move to next app */ + offset += sizeof(*app); + } +} + +/** + * i40e_parse_cee_tlv + * @tlv: CEE DCBX TLV + * @dcbcfg: Local store to update DCBX config data + * + * Get the TLV subtype and send it to parsing function + * based on the subtype value + **/ +static void i40e_parse_cee_tlv(struct i40e_lldp_org_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u16 len, tlvlen, sublen, typelength; + struct i40e_cee_feat_tlv *sub_tlv; + u8 subtype, feat_tlv_count = 0; + u32 ouisubtype; + + ouisubtype = ntohl(tlv->ouisubtype); + subtype = (u8)((ouisubtype & I40E_LLDP_TLV_SUBTYPE_MASK) >> + I40E_LLDP_TLV_SUBTYPE_SHIFT); + /* Return if not CEE DCBX */ + if (subtype != I40E_CEE_DCBX_TYPE) + return; + + typelength = ntohs(tlv->typelength); + tlvlen = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >> + I40E_LLDP_TLV_LEN_SHIFT); + len = sizeof(tlv->typelength) + sizeof(ouisubtype) + + sizeof(struct i40e_cee_ctrl_tlv); + /* Return if no CEE DCBX Feature TLVs */ + if (tlvlen <= len) + return; + + sub_tlv = (struct i40e_cee_feat_tlv *)((char *)tlv + len); + while (feat_tlv_count < I40E_CEE_MAX_FEAT_TYPE) { + typelength = ntohs(sub_tlv->hdr.typelen); + sublen = (u16)((typelength & + I40E_LLDP_TLV_LEN_MASK) >> + I40E_LLDP_TLV_LEN_SHIFT); + subtype = (u8)((typelength & I40E_LLDP_TLV_TYPE_MASK) >> + I40E_LLDP_TLV_TYPE_SHIFT); + switch (subtype) { + case I40E_CEE_SUBTYPE_PG_CFG: + i40e_parse_cee_pgcfg_tlv(sub_tlv, dcbcfg); + break; + case I40E_CEE_SUBTYPE_PFC_CFG: + i40e_parse_cee_pfccfg_tlv(sub_tlv, dcbcfg); + break; + case I40E_CEE_SUBTYPE_APP_PRI: + i40e_parse_cee_app_tlv(sub_tlv, dcbcfg); + break; + default: + return; /* Invalid Sub-type return */ + } + feat_tlv_count++; + /* Move to next sub TLV */ + sub_tlv = (struct i40e_cee_feat_tlv *)((char *)sub_tlv + + sizeof(sub_tlv->hdr.typelen) + + sublen); + } +} + +/** * i40e_parse_org_tlv * @tlv: Organization specific TLV * @dcbcfg: Local store to update ETS REC data @@ -312,6 +488,9 @@ static void i40e_parse_org_tlv(struct i40e_lldp_org_tlv *tlv, case I40E_IEEE_8021QAZ_OUI: i40e_parse_ieee_tlv(tlv, dcbcfg); break; + case I40E_CEE_DCBX_OUI: + i40e_parse_cee_tlv(tlv, dcbcfg); + break; default: break; } @@ -502,15 +681,18 @@ static void i40e_cee_to_dcb_config( /* CEE PG data to ETS config */ dcbcfg->etscfg.maxtcs = cee_cfg->oper_num_tc; + /* Note that the FW creates the oper_prio_tc nibbles reversed + * from those in the CEE Priority Group sub-TLV. + */ for (i = 0; i < 4; i++) { tc = (u8)((cee_cfg->oper_prio_tc[i] & - I40E_CEE_PGID_PRIO_1_MASK) >> - I40E_CEE_PGID_PRIO_1_SHIFT); - dcbcfg->etscfg.prioritytable[i*2] = tc; - tc = (u8)((cee_cfg->oper_prio_tc[i] & I40E_CEE_PGID_PRIO_0_MASK) >> I40E_CEE_PGID_PRIO_0_SHIFT); - dcbcfg->etscfg.prioritytable[i*2 + 1] = tc; + dcbcfg->etscfg.prioritytable[i * 2] = tc; + tc = (u8)((cee_cfg->oper_prio_tc[i] & + I40E_CEE_PGID_PRIO_1_MASK) >> + I40E_CEE_PGID_PRIO_1_SHIFT); + dcbcfg->etscfg.prioritytable[i * 2 + 1] = tc; } for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) @@ -531,37 +713,85 @@ static void i40e_cee_to_dcb_config( dcbcfg->pfc.pfcenable = cee_cfg->oper_pfc_en; dcbcfg->pfc.pfccap = I40E_MAX_TRAFFIC_CLASS; - status = (tlv_status & I40E_AQC_CEE_APP_STATUS_MASK) >> - I40E_AQC_CEE_APP_STATUS_SHIFT; + i = 0; + status = (tlv_status & I40E_AQC_CEE_FCOE_STATUS_MASK) >> + I40E_AQC_CEE_FCOE_STATUS_SHIFT; err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0; sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0; oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0; - /* Add APPs if Error is False and Oper/Sync is True */ + /* Add FCoE APP if Error is False and Oper/Sync is True */ if (!err && sync && oper) { - /* CEE operating configuration supports FCoE/iSCSI/FIP only */ - dcbcfg->numapps = I40E_CEE_OPER_MAX_APPS; - /* FCoE APP */ - dcbcfg->app[0].priority = + dcbcfg->app[i].priority = (app_prio & I40E_AQC_CEE_APP_FCOE_MASK) >> I40E_AQC_CEE_APP_FCOE_SHIFT; - dcbcfg->app[0].selector = I40E_APP_SEL_ETHTYPE; - dcbcfg->app[0].protocolid = I40E_APP_PROTOID_FCOE; + dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE; + dcbcfg->app[i].protocolid = I40E_APP_PROTOID_FCOE; + i++; + } + status = (tlv_status & I40E_AQC_CEE_ISCSI_STATUS_MASK) >> + I40E_AQC_CEE_ISCSI_STATUS_SHIFT; + err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0; + sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0; + oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0; + /* Add iSCSI APP if Error is False and Oper/Sync is True */ + if (!err && sync && oper) { /* iSCSI APP */ - dcbcfg->app[1].priority = + dcbcfg->app[i].priority = (app_prio & I40E_AQC_CEE_APP_ISCSI_MASK) >> I40E_AQC_CEE_APP_ISCSI_SHIFT; - dcbcfg->app[1].selector = I40E_APP_SEL_TCPIP; - dcbcfg->app[1].protocolid = I40E_APP_PROTOID_ISCSI; + dcbcfg->app[i].selector = I40E_APP_SEL_TCPIP; + dcbcfg->app[i].protocolid = I40E_APP_PROTOID_ISCSI; + i++; + } + status = (tlv_status & I40E_AQC_CEE_FIP_STATUS_MASK) >> + I40E_AQC_CEE_FIP_STATUS_SHIFT; + err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0; + sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0; + oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0; + /* Add FIP APP if Error is False and Oper/Sync is True */ + if (!err && sync && oper) { /* FIP APP */ - dcbcfg->app[2].priority = + dcbcfg->app[i].priority = (app_prio & I40E_AQC_CEE_APP_FIP_MASK) >> I40E_AQC_CEE_APP_FIP_SHIFT; - dcbcfg->app[2].selector = I40E_APP_SEL_ETHTYPE; - dcbcfg->app[2].protocolid = I40E_APP_PROTOID_FIP; + dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE; + dcbcfg->app[i].protocolid = I40E_APP_PROTOID_FIP; + i++; } + dcbcfg->numapps = i; +} + +/** + * i40e_get_ieee_dcb_config + * @hw: pointer to the hw struct + * + * Get IEEE mode DCB configuration from the Firmware + **/ +static i40e_status i40e_get_ieee_dcb_config(struct i40e_hw *hw) +{ + i40e_status ret = 0; + + /* IEEE mode */ + hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_IEEE; + /* Get Local DCB Config */ + ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_LOCAL, 0, + &hw->local_dcbx_config); + if (ret) + goto out; + + /* Get Remote DCB Config */ + ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE, + I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE, + &hw->remote_dcbx_config); + /* Don't treat ENOENT as an error for Remote MIBs */ + if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT) + ret = 0; + +out: + return ret; } /** @@ -579,7 +809,7 @@ i40e_status i40e_get_dcb_config(struct i40e_hw *hw) /* If Firmware version < v4.33 IEEE only */ if (((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver < 33)) || (hw->aq.fw_maj_ver < 4)) - goto ieee; + return i40e_get_ieee_dcb_config(hw); /* If Firmware version == v4.33 use old CEE struct */ if ((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver == 33)) { @@ -608,16 +838,14 @@ i40e_status i40e_get_dcb_config(struct i40e_hw *hw) /* CEE mode not enabled try querying IEEE data */ if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT) - goto ieee; - else + return i40e_get_ieee_dcb_config(hw); + + if (ret) goto out; -ieee: - /* IEEE mode */ - hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_IEEE; - /* Get Local DCB Config */ + /* Get CEE DCB Desired Config */ ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_LOCAL, 0, - &hw->local_dcbx_config); + &hw->desired_dcbx_config); if (ret) goto out; diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.h b/drivers/net/ethernet/intel/i40e/i40e_dcb.h index 50fc894a4cde..92d01042c1f6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.h +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.h @@ -44,6 +44,15 @@ #define I40E_IEEE_SUBTYPE_PFC_CFG 11 #define I40E_IEEE_SUBTYPE_APP_PRI 12 +#define I40E_CEE_DCBX_OUI 0x001b21 +#define I40E_CEE_DCBX_TYPE 2 + +#define I40E_CEE_SUBTYPE_CTRL 1 +#define I40E_CEE_SUBTYPE_PG_CFG 2 +#define I40E_CEE_SUBTYPE_PFC_CFG 3 +#define I40E_CEE_SUBTYPE_APP_PRI 4 + +#define I40E_CEE_MAX_FEAT_TYPE 3 /* Defines for LLDP TLV header */ #define I40E_LLDP_TLV_LEN_SHIFT 0 #define I40E_LLDP_TLV_LEN_MASK (0x01FF << I40E_LLDP_TLV_LEN_SHIFT) @@ -98,6 +107,36 @@ struct i40e_lldp_org_tlv { __be32 ouisubtype; u8 tlvinfo[1]; }; + +struct i40e_cee_tlv_hdr { + __be16 typelen; + u8 operver; + u8 maxver; +}; + +struct i40e_cee_ctrl_tlv { + struct i40e_cee_tlv_hdr hdr; + __be32 seqno; + __be32 ackno; +}; + +struct i40e_cee_feat_tlv { + struct i40e_cee_tlv_hdr hdr; + u8 en_will_err; /* Bits: |En|Will|Err|Reserved(5)| */ +#define I40E_CEE_FEAT_TLV_ENABLE_MASK 0x80 +#define I40E_CEE_FEAT_TLV_WILLING_MASK 0x40 +#define I40E_CEE_FEAT_TLV_ERR_MASK 0x20 + u8 subtype; + u8 tlvinfo[1]; +}; + +struct i40e_cee_app_prio { + __be16 protocol; + u8 upper_oui_sel; /* Bits: |Upper OUI(6)|Selector(2)| */ +#define I40E_CEE_APP_SELECTOR_MASK 0x03 + __be16 lower_oui; + u8 prio_map; +}; #pragma pack() i40e_status i40e_get_dcbx_status(struct i40e_hw *hw, diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c index 1c51f736a8d0..7c42d1340de6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c @@ -236,13 +236,13 @@ static void i40e_dcbnl_del_app(struct i40e_pf *pf, struct i40e_dcb_app_priority_table *app) { int v, err; + for (v = 0; v < pf->num_alloc_vsi; v++) { if (pf->vsi[v] && pf->vsi[v]->netdev) { err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app); if (err) - dev_info(&pf->pdev->dev, "%s: Failed deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", - __func__, pf->vsi[v]->seid, - err, app->selector, + dev_info(&pf->pdev->dev, "Failed deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", + pf->vsi[v]->seid, err, app->selector, app->protocolid, app->priority); } } diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index d7c15d17faa6..c1dd2483e262 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -404,82 +404,82 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) nstat = i40e_get_vsi_stats_struct(vsi); dev_info(&pf->pdev->dev, " net_stats: rx_packets = %lu, rx_bytes = %lu, rx_errors = %lu, rx_dropped = %lu\n", - (long unsigned int)nstat->rx_packets, - (long unsigned int)nstat->rx_bytes, - (long unsigned int)nstat->rx_errors, - (long unsigned int)nstat->rx_dropped); + (unsigned long int)nstat->rx_packets, + (unsigned long int)nstat->rx_bytes, + (unsigned long int)nstat->rx_errors, + (unsigned long int)nstat->rx_dropped); dev_info(&pf->pdev->dev, " net_stats: tx_packets = %lu, tx_bytes = %lu, tx_errors = %lu, tx_dropped = %lu\n", - (long unsigned int)nstat->tx_packets, - (long unsigned int)nstat->tx_bytes, - (long unsigned int)nstat->tx_errors, - (long unsigned int)nstat->tx_dropped); + (unsigned long int)nstat->tx_packets, + (unsigned long int)nstat->tx_bytes, + (unsigned long int)nstat->tx_errors, + (unsigned long int)nstat->tx_dropped); dev_info(&pf->pdev->dev, " net_stats: multicast = %lu, collisions = %lu\n", - (long unsigned int)nstat->multicast, - (long unsigned int)nstat->collisions); + (unsigned long int)nstat->multicast, + (unsigned long int)nstat->collisions); dev_info(&pf->pdev->dev, " net_stats: rx_length_errors = %lu, rx_over_errors = %lu, rx_crc_errors = %lu\n", - (long unsigned int)nstat->rx_length_errors, - (long unsigned int)nstat->rx_over_errors, - (long unsigned int)nstat->rx_crc_errors); + (unsigned long int)nstat->rx_length_errors, + (unsigned long int)nstat->rx_over_errors, + (unsigned long int)nstat->rx_crc_errors); dev_info(&pf->pdev->dev, " net_stats: rx_frame_errors = %lu, rx_fifo_errors = %lu, rx_missed_errors = %lu\n", - (long unsigned int)nstat->rx_frame_errors, - (long unsigned int)nstat->rx_fifo_errors, - (long unsigned int)nstat->rx_missed_errors); + (unsigned long int)nstat->rx_frame_errors, + (unsigned long int)nstat->rx_fifo_errors, + (unsigned long int)nstat->rx_missed_errors); dev_info(&pf->pdev->dev, " net_stats: tx_aborted_errors = %lu, tx_carrier_errors = %lu, tx_fifo_errors = %lu\n", - (long unsigned int)nstat->tx_aborted_errors, - (long unsigned int)nstat->tx_carrier_errors, - (long unsigned int)nstat->tx_fifo_errors); + (unsigned long int)nstat->tx_aborted_errors, + (unsigned long int)nstat->tx_carrier_errors, + (unsigned long int)nstat->tx_fifo_errors); dev_info(&pf->pdev->dev, " net_stats: tx_heartbeat_errors = %lu, tx_window_errors = %lu\n", - (long unsigned int)nstat->tx_heartbeat_errors, - (long unsigned int)nstat->tx_window_errors); + (unsigned long int)nstat->tx_heartbeat_errors, + (unsigned long int)nstat->tx_window_errors); dev_info(&pf->pdev->dev, " net_stats: rx_compressed = %lu, tx_compressed = %lu\n", - (long unsigned int)nstat->rx_compressed, - (long unsigned int)nstat->tx_compressed); + (unsigned long int)nstat->rx_compressed, + (unsigned long int)nstat->tx_compressed); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_packets = %lu, rx_bytes = %lu, rx_errors = %lu, rx_dropped = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_packets, - (long unsigned int)vsi->net_stats_offsets.rx_bytes, - (long unsigned int)vsi->net_stats_offsets.rx_errors, - (long unsigned int)vsi->net_stats_offsets.rx_dropped); + (unsigned long int)vsi->net_stats_offsets.rx_packets, + (unsigned long int)vsi->net_stats_offsets.rx_bytes, + (unsigned long int)vsi->net_stats_offsets.rx_errors, + (unsigned long int)vsi->net_stats_offsets.rx_dropped); dev_info(&pf->pdev->dev, " net_stats_offsets: tx_packets = %lu, tx_bytes = %lu, tx_errors = %lu, tx_dropped = %lu\n", - (long unsigned int)vsi->net_stats_offsets.tx_packets, - (long unsigned int)vsi->net_stats_offsets.tx_bytes, - (long unsigned int)vsi->net_stats_offsets.tx_errors, - (long unsigned int)vsi->net_stats_offsets.tx_dropped); + (unsigned long int)vsi->net_stats_offsets.tx_packets, + (unsigned long int)vsi->net_stats_offsets.tx_bytes, + (unsigned long int)vsi->net_stats_offsets.tx_errors, + (unsigned long int)vsi->net_stats_offsets.tx_dropped); dev_info(&pf->pdev->dev, " net_stats_offsets: multicast = %lu, collisions = %lu\n", - (long unsigned int)vsi->net_stats_offsets.multicast, - (long unsigned int)vsi->net_stats_offsets.collisions); + (unsigned long int)vsi->net_stats_offsets.multicast, + (unsigned long int)vsi->net_stats_offsets.collisions); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_length_errors = %lu, rx_over_errors = %lu, rx_crc_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_length_errors, - (long unsigned int)vsi->net_stats_offsets.rx_over_errors, - (long unsigned int)vsi->net_stats_offsets.rx_crc_errors); + (unsigned long int)vsi->net_stats_offsets.rx_length_errors, + (unsigned long int)vsi->net_stats_offsets.rx_over_errors, + (unsigned long int)vsi->net_stats_offsets.rx_crc_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_frame_errors = %lu, rx_fifo_errors = %lu, rx_missed_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_frame_errors, - (long unsigned int)vsi->net_stats_offsets.rx_fifo_errors, - (long unsigned int)vsi->net_stats_offsets.rx_missed_errors); + (unsigned long int)vsi->net_stats_offsets.rx_frame_errors, + (unsigned long int)vsi->net_stats_offsets.rx_fifo_errors, + (unsigned long int)vsi->net_stats_offsets.rx_missed_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: tx_aborted_errors = %lu, tx_carrier_errors = %lu, tx_fifo_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.tx_aborted_errors, - (long unsigned int)vsi->net_stats_offsets.tx_carrier_errors, - (long unsigned int)vsi->net_stats_offsets.tx_fifo_errors); + (unsigned long int)vsi->net_stats_offsets.tx_aborted_errors, + (unsigned long int)vsi->net_stats_offsets.tx_carrier_errors, + (unsigned long int)vsi->net_stats_offsets.tx_fifo_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: tx_heartbeat_errors = %lu, tx_window_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.tx_heartbeat_errors, - (long unsigned int)vsi->net_stats_offsets.tx_window_errors); + (unsigned long int)vsi->net_stats_offsets.tx_heartbeat_errors, + (unsigned long int)vsi->net_stats_offsets.tx_window_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_compressed = %lu, tx_compressed = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_compressed, - (long unsigned int)vsi->net_stats_offsets.tx_compressed); + (unsigned long int)vsi->net_stats_offsets.rx_compressed, + (unsigned long int)vsi->net_stats_offsets.tx_compressed); dev_info(&pf->pdev->dev, " tx_restart = %d, tx_busy = %d, rx_buf_failed = %d, rx_page_failed = %d\n", vsi->tx_restart, vsi->tx_busy, @@ -487,6 +487,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) rcu_read_lock(); for (i = 0; i < vsi->num_queue_pairs; i++) { struct i40e_ring *rx_ring = ACCESS_ONCE(vsi->rx_rings[i]); + if (!rx_ring) continue; @@ -527,7 +528,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) dev_info(&pf->pdev->dev, " rx_rings[%i]: size = %i, dma = 0x%08lx\n", i, rx_ring->size, - (long unsigned int)rx_ring->dma); + (unsigned long int)rx_ring->dma); dev_info(&pf->pdev->dev, " rx_rings[%i]: vsi = %p, q_vector = %p\n", i, rx_ring->vsi, @@ -535,6 +536,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) } for (i = 0; i < vsi->num_queue_pairs; i++) { struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[i]); + if (!tx_ring) continue; @@ -573,7 +575,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) dev_info(&pf->pdev->dev, " tx_rings[%i]: size = %i, dma = 0x%08lx\n", i, tx_ring->size, - (long unsigned int)tx_ring->dma); + (unsigned long int)tx_ring->dma); dev_info(&pf->pdev->dev, " tx_rings[%i]: vsi = %p, q_vector = %p\n", i, tx_ring->vsi, @@ -743,6 +745,7 @@ static void i40e_dbg_dump_aq_desc(struct i40e_pf *pf) ring = &(hw->aq.asq); for (i = 0; i < ring->count; i++) { struct i40e_aq_desc *d = I40E_ADMINQ_DESC(*ring, i); + dev_info(&pf->pdev->dev, " at[%02d] flags=0x%04x op=0x%04x dlen=0x%04x ret=0x%04x cookie_h=0x%08x cookie_l=0x%08x\n", i, d->flags, d->opcode, d->datalen, d->retval, @@ -755,6 +758,7 @@ static void i40e_dbg_dump_aq_desc(struct i40e_pf *pf) ring = &(hw->aq.arq); for (i = 0; i < ring->count; i++) { struct i40e_aq_desc *d = I40E_ADMINQ_DESC(*ring, i); + dev_info(&pf->pdev->dev, " ar[%02d] flags=0x%04x op=0x%04x dlen=0x%04x ret=0x%04x cookie_h=0x%08x cookie_l=0x%08x\n", i, d->flags, d->opcode, d->datalen, d->retval, @@ -1038,7 +1042,13 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, "'%s' failed\n", cmd_buf); } else if (strncmp(cmd_buf, "del vsi", 7) == 0) { - sscanf(&cmd_buf[7], "%i", &vsi_seid); + cnt = sscanf(&cmd_buf[7], "%i", &vsi_seid); + if (cnt != 1) { + dev_info(&pf->pdev->dev, + "del vsi: bad command string, cnt=%d\n", + cnt); + goto command_write_done; + } vsi = i40e_dbg_find_vsi(pf, vsi_seid); if (!vsi) { dev_info(&pf->pdev->dev, "del VSI %d: seid not found\n", @@ -1146,7 +1156,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } f = i40e_add_filter(vsi, ma, vlan, false, false); - ret = i40e_sync_vsi_filters(vsi); + ret = i40e_sync_vsi_filters(vsi, true); if (f && !ret) dev_info(&pf->pdev->dev, "add macaddr: %pM vlan=%d added to VSI %d\n", @@ -1183,7 +1193,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } i40e_del_filter(vsi, ma, vlan, false, false); - ret = i40e_sync_vsi_filters(vsi); + ret = i40e_sync_vsi_filters(vsi, true); if (!ret) dev_info(&pf->pdev->dev, "del macaddr: %pM vlan=%d removed from VSI %d\n", @@ -1488,6 +1498,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "read", 4) == 0) { u32 address; u32 value; + cnt = sscanf(&cmd_buf[4], "%i", &address); if (cnt != 1) { dev_info(&pf->pdev->dev, "read <reg>\n"); @@ -1495,9 +1506,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } /* check the range on address */ - if (address >= I40E_MAX_REGISTER) { - dev_info(&pf->pdev->dev, "read reg address 0x%08x too large\n", - address); + if (address > (pf->ioremap_len - sizeof(u32))) { + dev_info(&pf->pdev->dev, "read reg address 0x%08x too large, max=0x%08lx\n", + address, (unsigned long int)(pf->ioremap_len - sizeof(u32))); goto command_write_done; } @@ -1507,6 +1518,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "write", 5) == 0) { u32 address, value; + cnt = sscanf(&cmd_buf[5], "%i %i", &address, &value); if (cnt != 2) { dev_info(&pf->pdev->dev, "write <reg> <value>\n"); @@ -1514,9 +1526,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } /* check the range on address */ - if (address >= I40E_MAX_REGISTER) { - dev_info(&pf->pdev->dev, "write reg address 0x%08x too large\n", - address); + if (address > (pf->ioremap_len - sizeof(u32))) { + dev_info(&pf->pdev->dev, "write reg address 0x%08x too large, max=0x%08lx\n", + address, (unsigned long int)(pf->ioremap_len - sizeof(u32))); goto command_write_done; } wr32(&pf->hw, address, value); @@ -1528,6 +1540,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, cnt = sscanf(&cmd_buf[15], "%i", &vsi_seid); if (cnt == 0) { int i; + for (i = 0; i < pf->num_alloc_vsi; i++) i40e_vsi_reset_stats(pf->vsi[i]); dev_info(&pf->pdev->dev, "vsi clear stats called for all vsi's\n"); @@ -1726,8 +1739,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, packet_len, I40E_FDIR_MAX_RAW_PACKET_SIZE); for (i = 0; i < packet_len; i++) { - sscanf(&asc_packet[j], "%2hhx ", - &raw_packet[i]); + cnt = sscanf(&asc_packet[j], "%2hhx ", &raw_packet[i]); + if (!cnt) + break; j += 3; } dev_info(&pf->pdev->dev, "FD raw packet dump\n"); @@ -1755,6 +1769,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "lldp", 4) == 0) { if (strncmp(&cmd_buf[5], "stop", 4) == 0) { int ret; + ret = i40e_aq_stop_lldp(&pf->hw, false, NULL); if (ret) { dev_info(&pf->pdev->dev, @@ -1779,6 +1794,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, #endif /* CONFIG_I40E_DCB */ } else if (strncmp(&cmd_buf[5], "start", 5) == 0) { int ret; + ret = i40e_aq_add_rem_control_packet_filter(&pf->hw, pf->hw.mac.addr, I40E_ETH_P_LLDP, 0, @@ -1807,6 +1823,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, u16 llen, rlen; int ret; u8 *buff; + buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL); if (!buff) goto command_write_done; @@ -1833,6 +1850,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, u16 llen, rlen; int ret; u8 *buff; + buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL); if (!buff) goto command_write_done; @@ -1858,6 +1876,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, buff = NULL; } else if (strncmp(&cmd_buf[5], "event on", 8) == 0) { int ret; + ret = i40e_aq_cfg_lldp_mib_change_event(&pf->hw, true, NULL); if (ret) { @@ -1868,6 +1887,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } } else if (strncmp(&cmd_buf[5], "event off", 9) == 0) { int ret; + ret = i40e_aq_cfg_lldp_mib_change_event(&pf->hw, false, NULL); if (ret) { @@ -2105,6 +2125,7 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, } } else if (strncmp(i40e_dbg_netdev_ops_buf, "change_mtu", 10) == 0) { int mtu; + cnt = sscanf(&i40e_dbg_netdev_ops_buf[11], "%i %i", &vsi_seid, &mtu); if (cnt != 2) { @@ -2220,7 +2241,6 @@ void i40e_dbg_pf_init(struct i40e_pf *pf) create_failed: dev_info(dev, "debugfs dir/file for %s failed\n", name); debugfs_remove_recursive(pf->i40e_dbg_pf); - return; } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index e972b5ecbf0b..148f61461076 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -87,6 +87,7 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = { I40E_VSI_STAT("rx_broadcast", eth_stats.rx_broadcast), I40E_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast), I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol), + I40E_VSI_STAT("tx_linearize", tx_linearize), }; static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, @@ -229,10 +230,10 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = { static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = { "NPAR", + "LinkPolling", }; -#define I40E_PRIV_FLAGS_STR_LEN \ - (sizeof(i40e_priv_flags_strings) / ETH_GSTRING_LEN) +#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings) /** * i40e_partition_setting_complaint - generic complaint for MFP restriction @@ -425,6 +426,7 @@ static void i40e_get_settings_link_down(struct i40e_hw *hw, ecmd->advertising = ADVERTISED_10000baseKR_Full; break; case I40E_DEV_ID_10G_BASE_T: + case I40E_DEV_ID_10G_BASE_T4: ecmd->supported = SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full; @@ -437,6 +439,7 @@ static void i40e_get_settings_link_down(struct i40e_hw *hw, ecmd->advertising |= ADVERTISED_100baseT_Full; break; case I40E_DEV_ID_20G_KR2: + case I40E_DEV_ID_20G_KR2_A: /* backplane 20G */ ecmd->supported = SUPPORTED_20000baseKR2_Full; ecmd->advertising = ADVERTISED_20000baseKR2_Full; @@ -664,6 +667,13 @@ static int i40e_set_settings(struct net_device *netdev, advertise & ADVERTISED_40000baseLR4_Full) config.link_speed |= I40E_LINK_SPEED_40GB; + /* If speed didn't get set, set it to what it currently is. + * This is needed because if advertise is 0 (as it is when autoneg + * is disabled) then speed won't get set. + */ + if (!config.link_speed) + config.link_speed = abilities.link_speed; + if (change || (abilities.link_speed != config.link_speed)) { /* copy over the rest of the abilities */ config.phy_type = abilities.phy_type; @@ -680,7 +690,7 @@ static int i40e_set_settings(struct net_device *netdev, /* Tell the OS link is going down, the link will go * back up when fw says it is ready asynchronously */ - netdev_info(netdev, "PHY settings change requested, NIC Link is going down.\n"); + i40e_print_link_message(vsi, false); netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); } @@ -824,7 +834,7 @@ static int i40e_set_pauseparam(struct net_device *netdev, /* Tell the OS link is going down, the link will go back up when fw * says it is ready asynchronously */ - netdev_info(netdev, "Flow control settings change requested, NIC Link is going down.\n"); + i40e_print_link_message(vsi, false); netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); @@ -1166,6 +1176,11 @@ static int i40e_set_ringparam(struct net_device *netdev, /* clone ring and setup updated count */ tx_rings[i] = *vsi->tx_rings[i]; tx_rings[i].count = new_tx_count; + /* the desc and bi pointers will be reallocated in the + * setup call + */ + tx_rings[i].desc = NULL; + tx_rings[i].rx_bi = NULL; err = i40e_setup_tx_descriptors(&tx_rings[i]); if (err) { while (i) { @@ -1196,6 +1211,11 @@ static int i40e_set_ringparam(struct net_device *netdev, /* clone ring and setup updated count */ rx_rings[i] = *vsi->rx_rings[i]; rx_rings[i].count = new_rx_count; + /* the desc and bi pointers will be reallocated in the + * setup call + */ + rx_rings[i].desc = NULL; + rx_rings[i].rx_bi = NULL; err = i40e_setup_rx_descriptors(&rx_rings[i]); if (err) { while (i) { @@ -1263,7 +1283,8 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset) if (vsi == pf->vsi[pf->lan_vsi] && pf->hw.partition_id == 1) { int len = I40E_PF_STATS_LEN(netdev); - if (pf->lan_veb != I40E_NO_VEB) + if ((pf->lan_veb != I40E_NO_VEB) && + (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) len += I40E_VEB_STATS_TOTAL; return len; } else { @@ -1336,8 +1357,10 @@ static void i40e_get_ethtool_stats(struct net_device *netdev, if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1) return; - if (pf->lan_veb != I40E_NO_VEB) { + if ((pf->lan_veb != I40E_NO_VEB) && + (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) { struct i40e_veb *veb = pf->veb[pf->lan_veb]; + for (j = 0; j < I40E_VEB_STATS_LEN; j++) { p = (char *)veb; p += i40e_gstrings_veb_stats[j].stat_offset; @@ -1409,7 +1432,8 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1) return; - if (pf->lan_veb != I40E_NO_VEB) { + if ((pf->lan_veb != I40E_NO_VEB) && + (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) { for (i = 0; i < I40E_VEB_STATS_LEN; i++) { snprintf(p, ETH_GSTRING_LEN, "veb.%s", i40e_gstrings_veb_stats[i].stat_string); @@ -1504,9 +1528,18 @@ static int i40e_link_test(struct net_device *netdev, u64 *data) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; + i40e_status status; + bool link_up = false; netif_info(pf, hw, netdev, "link test\n"); - if (i40e_get_link_status(&pf->hw)) + status = i40e_get_link_status(&pf->hw, &link_up); + if (status) { + netif_err(pf, drv, netdev, "link query timed out, please retry test\n"); + *data = 1; + return *data; + } + + if (link_up) *data = 0; else *data = 1; @@ -1575,7 +1608,7 @@ static inline bool i40e_active_vfs(struct i40e_pf *pf) int i; for (i = 0; i < pf->num_alloc_vfs; i++) - if (vfs[i].vf_states & I40E_VF_STAT_ACTIVE) + if (test_bit(I40E_VF_STAT_ACTIVE, &vfs[i].vf_states)) return true; return false; } @@ -2604,10 +2637,31 @@ static u32 i40e_get_priv_flags(struct net_device *dev) ret_flags |= pf->hw.func_caps.npar_enable ? I40E_PRIV_FLAGS_NPAR_FLAG : 0; + ret_flags |= pf->flags & I40E_FLAG_LINK_POLLING_ENABLED ? + I40E_PRIV_FLAGS_LINKPOLL_FLAG : 0; return ret_flags; } +/** + * i40e_set_priv_flags - set private flags + * @dev: network interface device structure + * @flags: bit flags to be set + **/ +static int i40e_set_priv_flags(struct net_device *dev, u32 flags) +{ + struct i40e_netdev_priv *np = netdev_priv(dev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + + if (flags & I40E_PRIV_FLAGS_LINKPOLL_FLAG) + pf->flags |= I40E_FLAG_LINK_POLLING_ENABLED; + else + pf->flags &= ~I40E_FLAG_LINK_POLLING_ENABLED; + + return 0; +} + static const struct ethtool_ops i40e_ethtool_ops = { .get_settings = i40e_get_settings, .set_settings = i40e_set_settings, @@ -2644,6 +2698,7 @@ static const struct ethtool_ops i40e_ethtool_ops = { .set_channels = i40e_set_channels, .get_ts_info = i40e_get_ts_info, .get_priv_flags = i40e_get_priv_flags, + .set_priv_flags = i40e_set_priv_flags, }; void i40e_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c index 5ea75dd537d6..eaedc1f4c257 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c +++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c @@ -272,10 +272,8 @@ out: /** * i40e_fcoe_sw_init - sets up the HW for FCoE * @pf: pointer to PF - * - * Returns 0 if FCoE is supported otherwise the error code **/ -int i40e_init_pf_fcoe(struct i40e_pf *pf) +void i40e_init_pf_fcoe(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; u32 val; @@ -287,13 +285,13 @@ int i40e_init_pf_fcoe(struct i40e_pf *pf) if (!pf->hw.func_caps.fcoe) { dev_info(&pf->pdev->dev, "FCoE capability is disabled\n"); - return 0; + return; } if (!pf->hw.func_caps.dcb) { dev_warn(&pf->pdev->dev, "Hardware is not DCB capable not enabling FCoE.\n"); - return 0; + return; } /* enable FCoE hash filter */ @@ -326,7 +324,6 @@ int i40e_init_pf_fcoe(struct i40e_pf *pf) wr32(hw, I40E_GLFCOE_RCTL, val); dev_info(&pf->pdev->dev, "FCoE is supported.\n"); - return 0; } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c index fa371a2a40c6..79ae7beeafe5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c +++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c @@ -431,9 +431,8 @@ exit_sd_error: pd_idx1 = max(pd_idx, ((j - 1) * I40E_HMC_MAX_BP_COUNT)); pd_lmt1 = min(pd_lmt, (j * I40E_HMC_MAX_BP_COUNT)); - for (i = pd_idx1; i < pd_lmt1; i++) { + for (i = pd_idx1; i < pd_lmt1; i++) i40e_remove_pd_bp(hw, info->hmc_info, i); - } i40e_remove_pd_page(hw, info->hmc_info, (j - 1)); break; case I40E_SD_TYPE_DIRECT: diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 851c1a159be8..a484f2265524 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -39,7 +39,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 3 -#define DRV_VERSION_BUILD 9 +#define DRV_VERSION_BUILD 21 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN @@ -75,10 +75,13 @@ static const struct pci_device_id i40e_pci_tbl[] = { {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T4), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X722), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_X722), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_X722), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2_A), 0}, /* required last entry */ {0, } }; @@ -213,10 +216,10 @@ static int i40e_get_lump(struct i40e_pf *pf, struct i40e_lump_tracking *pile, ret = i; pile->search_hint = i + j; break; - } else { - /* not enough, so skip over it and continue looking */ - i += j; } + + /* not enough, so skip over it and continue looking */ + i += j; } return ret; @@ -299,25 +302,69 @@ static void i40e_tx_timeout(struct net_device *netdev) struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; + struct i40e_ring *tx_ring = NULL; + unsigned int i, hung_queue = 0; + u32 head, val; pf->tx_timeout_count++; + /* find the stopped queue the same way the stack does */ + for (i = 0; i < netdev->num_tx_queues; i++) { + struct netdev_queue *q; + unsigned long trans_start; + + q = netdev_get_tx_queue(netdev, i); + trans_start = q->trans_start ? : netdev->trans_start; + if (netif_xmit_stopped(q) && + time_after(jiffies, + (trans_start + netdev->watchdog_timeo))) { + hung_queue = i; + break; + } + } + + if (i == netdev->num_tx_queues) { + netdev_info(netdev, "tx_timeout: no netdev hung queue found\n"); + } else { + /* now that we have an index, find the tx_ring struct */ + for (i = 0; i < vsi->num_queue_pairs; i++) { + if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc) { + if (hung_queue == + vsi->tx_rings[i]->queue_index) { + tx_ring = vsi->tx_rings[i]; + break; + } + } + } + } + if (time_after(jiffies, (pf->tx_timeout_last_recovery + HZ*20))) - pf->tx_timeout_recovery_level = 1; + pf->tx_timeout_recovery_level = 1; /* reset after some time */ + else if (time_before(jiffies, + (pf->tx_timeout_last_recovery + netdev->watchdog_timeo))) + return; /* don't do any new action before the next timeout */ + + if (tx_ring) { + head = i40e_get_head(tx_ring); + /* Read interrupt register */ + if (pf->flags & I40E_FLAG_MSIX_ENABLED) + val = rd32(&pf->hw, + I40E_PFINT_DYN_CTLN(tx_ring->q_vector->v_idx + + tx_ring->vsi->base_vector - 1)); + else + val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0); + + netdev_info(netdev, "tx_timeout: VSI_seid: %d, Q %d, NTC: 0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x, INT: 0x%x\n", + vsi->seid, hung_queue, tx_ring->next_to_clean, + head, tx_ring->next_to_use, + readl(tx_ring->tail), val); + } + pf->tx_timeout_last_recovery = jiffies; - netdev_info(netdev, "tx_timeout recovery level %d\n", - pf->tx_timeout_recovery_level); + netdev_info(netdev, "tx_timeout recovery level %d, hung_queue %d\n", + pf->tx_timeout_recovery_level, hung_queue); switch (pf->tx_timeout_recovery_level) { - case 0: - /* disable and re-enable queues for the VSI */ - if (in_interrupt()) { - set_bit(__I40E_REINIT_REQUESTED, &pf->state); - set_bit(__I40E_REINIT_REQUESTED, &vsi->state); - } else { - i40e_vsi_reinit_locked(vsi); - } - break; case 1: set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); break; @@ -329,10 +376,9 @@ static void i40e_tx_timeout(struct net_device *netdev) break; default: netdev_err(netdev, "tx_timeout recovery unsuccessful\n"); - set_bit(__I40E_DOWN_REQUESTED, &pf->state); - set_bit(__I40E_DOWN_REQUESTED, &vsi->state); break; } + i40e_service_event_schedule(pf); pf->tx_timeout_recovery_level++; } @@ -431,6 +477,7 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct( stats->tx_errors = vsi_stats->tx_errors; stats->tx_dropped = vsi_stats->tx_dropped; stats->rx_errors = vsi_stats->rx_errors; + stats->rx_dropped = vsi_stats->rx_dropped; stats->rx_crc_errors = vsi_stats->rx_crc_errors; stats->rx_length_errors = vsi_stats->rx_length_errors; @@ -456,11 +503,11 @@ void i40e_vsi_reset_stats(struct i40e_vsi *vsi) memset(&vsi->eth_stats_offsets, 0, sizeof(vsi->eth_stats_offsets)); if (vsi->rx_rings && vsi->rx_rings[0]) { for (i = 0; i < vsi->num_queue_pairs; i++) { - memset(&vsi->rx_rings[i]->stats, 0 , + memset(&vsi->rx_rings[i]->stats, 0, sizeof(vsi->rx_rings[i]->stats)); - memset(&vsi->rx_rings[i]->rx_stats, 0 , + memset(&vsi->rx_rings[i]->rx_stats, 0, sizeof(vsi->rx_rings[i]->rx_stats)); - memset(&vsi->tx_rings[i]->stats, 0 , + memset(&vsi->tx_rings[i]->stats, 0, sizeof(vsi->tx_rings[i]->stats)); memset(&vsi->tx_rings[i]->tx_stats, 0, sizeof(vsi->tx_rings[i]->tx_stats)); @@ -754,7 +801,6 @@ static void i40e_update_link_xoff_rx(struct i40e_pf *pf) struct i40e_hw_port_stats *nsd = &pf->stats; struct i40e_hw *hw = &pf->hw; u64 xoff = 0; - u16 i, v; if ((hw->fc.current_mode != I40E_FC_FULL) && (hw->fc.current_mode != I40E_FC_RX_PAUSE)) @@ -769,18 +815,6 @@ static void i40e_update_link_xoff_rx(struct i40e_pf *pf) if (!(nsd->link_xoff_rx - xoff)) return; - /* Clear the __I40E_HANG_CHECK_ARMED bit for all Tx rings */ - for (v = 0; v < pf->num_alloc_vsi; v++) { - struct i40e_vsi *vsi = pf->vsi[v]; - - if (!vsi || !vsi->tx_rings[0]) - continue; - - for (i = 0; i < vsi->num_queue_pairs; i++) { - struct i40e_ring *ring = vsi->tx_rings[i]; - clear_bit(__I40E_HANG_CHECK_ARMED, &ring->state); - } - } } /** @@ -796,7 +830,7 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf) bool xoff[I40E_MAX_TRAFFIC_CLASS] = {false}; struct i40e_dcbx_config *dcb_cfg; struct i40e_hw *hw = &pf->hw; - u16 i, v; + u16 i; u8 tc; dcb_cfg = &hw->local_dcbx_config; @@ -809,6 +843,7 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf) for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { u64 prio_xoff = nsd->priority_xoff_rx[i]; + i40e_stat_update32(hw, I40E_GLPRT_PXOFFRXC(hw->port, i), pf->stat_offsets_loaded, &osd->priority_xoff_rx[i], @@ -821,23 +856,6 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf) tc = dcb_cfg->etscfg.prioritytable[i]; xoff[tc] = true; } - - /* Clear the __I40E_HANG_CHECK_ARMED bit for Tx rings */ - for (v = 0; v < pf->num_alloc_vsi; v++) { - struct i40e_vsi *vsi = pf->vsi[v]; - - if (!vsi || !vsi->tx_rings[0]) - continue; - - for (i = 0; i < vsi->num_queue_pairs; i++) { - struct i40e_ring *ring = vsi->tx_rings[i]; - - tc = ring->dcb_tc; - if (xoff[tc]) - clear_bit(__I40E_HANG_CHECK_ARMED, - &ring->state); - } - } } /** @@ -862,6 +880,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) u32 rx_page, rx_buf; u64 bytes, packets; unsigned int start; + u64 tx_linearize; u64 rx_p, rx_b; u64 tx_p, tx_b; u16 q; @@ -880,7 +899,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) */ rx_b = rx_p = 0; tx_b = tx_p = 0; - tx_restart = tx_busy = 0; + tx_restart = tx_busy = tx_linearize = 0; rx_page = 0; rx_buf = 0; rcu_read_lock(); @@ -897,6 +916,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) tx_p += packets; tx_restart += p->tx_stats.restart_queue; tx_busy += p->tx_stats.tx_busy; + tx_linearize += p->tx_stats.tx_linearize; /* Rx queue is part of the same block as Tx queue */ p = &p[1]; @@ -913,6 +933,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) rcu_read_unlock(); vsi->tx_restart = tx_restart; vsi->tx_busy = tx_busy; + vsi->tx_linearize = tx_linearize; vsi->rx_page_failed = rx_page; vsi->rx_buf_failed = rx_buf; @@ -1256,7 +1277,7 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) * so we have to go through all the list in order to make sure */ list_for_each_entry(f, &vsi->mac_filter_list, list) { - if (f->vlan >= 0) + if (f->vlan >= 0 || vsi->info.pvid) return true; } @@ -1419,6 +1440,7 @@ void i40e_del_filter(struct i40e_vsi *vsi, } else { /* make sure we don't remove a filter in use by VF or netdev */ int min_f = 0; + min_f += (f->is_vf ? 1 : 0); min_f += (f->is_netdev ? 1 : 0); @@ -1477,6 +1499,7 @@ static int i40e_set_mac(struct net_device *netdev, void *p) if (vsi->type == I40E_VSI_MAIN) { i40e_status ret; + ret = i40e_aq_mac_address_write(&vsi->back->hw, I40E_AQC_WRITE_TYPE_LAA_WOL, addr->sa_data, NULL); @@ -1514,7 +1537,7 @@ static int i40e_set_mac(struct net_device *netdev, void *p) f->is_laa = true; } - i40e_sync_vsi_filters(vsi); + i40e_sync_vsi_filters(vsi, false); ether_addr_copy(netdev->dev_addr, addr->sa_data); return 0; @@ -1709,36 +1732,27 @@ static void i40e_set_rx_mode(struct net_device *netdev) /* remove filter if not in netdev list */ list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { - bool found = false; if (!f->is_netdev) continue; - if (is_multicast_ether_addr(f->macaddr)) { - netdev_for_each_mc_addr(mca, netdev) { - if (ether_addr_equal(mca->addr, f->macaddr)) { - found = true; - break; - } - } - } else { - netdev_for_each_uc_addr(uca, netdev) { - if (ether_addr_equal(uca->addr, f->macaddr)) { - found = true; - break; - } - } + netdev_for_each_mc_addr(mca, netdev) + if (ether_addr_equal(mca->addr, f->macaddr)) + goto bottom_of_search_loop; - for_each_dev_addr(netdev, ha) { - if (ether_addr_equal(ha->addr, f->macaddr)) { - found = true; - break; - } - } - } - if (!found) - i40e_del_filter( - vsi, f->macaddr, I40E_VLAN_ANY, false, true); + netdev_for_each_uc_addr(uca, netdev) + if (ether_addr_equal(uca->addr, f->macaddr)) + goto bottom_of_search_loop; + + for_each_dev_addr(netdev, ha) + if (ether_addr_equal(ha->addr, f->macaddr)) + goto bottom_of_search_loop; + + /* f->macaddr wasn't found in uc, mc, or ha list so delete it */ + i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY, false, true); + +bottom_of_search_loop: + continue; } /* check for other flag changes */ @@ -1751,12 +1765,13 @@ static void i40e_set_rx_mode(struct net_device *netdev) /** * i40e_sync_vsi_filters - Update the VSI filter list to the HW * @vsi: ptr to the VSI + * @grab_rtnl: whether RTNL needs to be grabbed * * Push any outstanding VSI filter changes through the AdminQ. * * Returns 0 or error value **/ -int i40e_sync_vsi_filters(struct i40e_vsi *vsi) +int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl) { struct i40e_mac_filter *f, *ftmp; bool promisc_forced_on = false; @@ -1920,6 +1935,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* check for changes in promiscuous modes */ if (changed_flags & IFF_ALLMULTI) { bool cur_multipromisc; + cur_multipromisc = !!(vsi->current_netdev_flags & IFF_ALLMULTI); ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw, vsi->seid, @@ -1934,6 +1950,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) } if ((changed_flags & IFF_PROMISC) || promisc_forced_on) { bool cur_promisc; + cur_promisc = (!!(vsi->current_netdev_flags & IFF_PROMISC) || test_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state)); @@ -1945,7 +1962,11 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) */ if (pf->cur_promisc != cur_promisc) { pf->cur_promisc = cur_promisc; - i40e_do_reset_safe(pf, + if (grab_rtnl) + i40e_do_reset_safe(pf, + BIT(__I40E_PF_RESET_REQUESTED)); + else + i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED)); } } else { @@ -1996,7 +2017,7 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf) for (v = 0; v < pf->num_alloc_vsi; v++) { if (pf->vsi[v] && (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED)) - i40e_sync_vsi_filters(pf->vsi[v]); + i40e_sync_vsi_filters(pf->vsi[v], true); } } @@ -2203,7 +2224,7 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) return 0; - return i40e_sync_vsi_filters(vsi); + return i40e_sync_vsi_filters(vsi, false); } /** @@ -2275,7 +2296,7 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) return 0; - return i40e_sync_vsi_filters(vsi); + return i40e_sync_vsi_filters(vsi, false); } /** @@ -2609,8 +2630,6 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) wr32(hw, I40E_QTX_CTL(pf_q), qtx_ctl); i40e_flush(hw); - clear_bit(__I40E_HANG_CHECK_ARMED, &ring->state); - /* cache tail off for easier writes later */ ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); @@ -2672,7 +2691,8 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) rx_ctx.lrxqthresh = 2; rx_ctx.crcstrip = 1; rx_ctx.l2tsel = 1; - rx_ctx.showiv = 1; + /* this controls whether VLAN is stripped from inner headers */ + rx_ctx.showiv = 0; #ifdef I40E_FCOE rx_ctx.fc_ena = (vsi->type == I40E_VSI_FCOE); #endif @@ -3045,24 +3065,6 @@ void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf) } /** - * i40e_irq_dynamic_enable - Enable default interrupt generation settings - * @vsi: pointer to a vsi - * @vector: enable a particular Hw Interrupt vector - **/ -void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector) -{ - struct i40e_pf *pf = vsi->back; - struct i40e_hw *hw = &pf->hw; - u32 val; - - val = I40E_PFINT_DYN_CTLN_INTENA_MASK | - I40E_PFINT_DYN_CTLN_CLEARPBA_MASK | - (I40E_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT); - wr32(hw, I40E_PFINT_DYN_CTLN(vector - 1), val); - /* skip the flush */ -} - -/** * i40e_irq_dynamic_disable - Disable default interrupt generation settings * @vsi: pointer to a vsi * @vector: disable a particular Hw Interrupt vector @@ -3135,8 +3137,7 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) q_vector); if (err) { dev_info(&pf->pdev->dev, - "%s: request_irq failed, error: %d\n", - __func__, err); + "MSIX request_irq failed, error: %d\n", err); goto free_queue_irqs; } /* assign the mask for this irq */ @@ -3201,8 +3202,7 @@ static int i40e_vsi_enable_irq(struct i40e_vsi *vsi) int i; if (pf->flags & I40E_FLAG_MSIX_ENABLED) { - for (i = vsi->base_vector; - i < (vsi->num_q_vectors + vsi->base_vector); i++) + for (i = 0; i < vsi->num_q_vectors; i++) i40e_irq_dynamic_enable(vsi, i); } else { i40e_irq_dynamic_enable_icr0(pf); @@ -3264,6 +3264,7 @@ static irqreturn_t i40e_intr(int irq, void *data) /* temporarily disable queue cause for NAPI processing */ u32 qval = rd32(hw, I40E_QINT_RQCTL(0)); + qval &= ~I40E_QINT_RQCTL_CAUSE_ENA_MASK; wr32(hw, I40E_QINT_RQCTL(0), qval); @@ -3433,10 +3434,9 @@ static bool i40e_clean_fdir_tx_irq(struct i40e_ring *tx_ring, int budget) i += tx_ring->count; tx_ring->next_to_clean = i; - if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) { - i40e_irq_dynamic_enable(vsi, - tx_ring->q_vector->v_idx + vsi->base_vector); - } + if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) + i40e_irq_dynamic_enable(vsi, tx_ring->q_vector->v_idx); + return budget > 0; } @@ -3662,9 +3662,8 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable) ret = i40e_pf_txq_wait(pf, pf_q, enable); if (ret) { dev_info(&pf->pdev->dev, - "%s: VSI seid %d Tx ring %d %sable timeout\n", - __func__, vsi->seid, pf_q, - (enable ? "en" : "dis")); + "VSI seid %d Tx ring %d %sable timeout\n", + vsi->seid, pf_q, (enable ? "en" : "dis")); break; } } @@ -3740,9 +3739,8 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable) ret = i40e_pf_rxq_wait(pf, pf_q, enable); if (ret) { dev_info(&pf->pdev->dev, - "%s: VSI seid %d Rx ring %d %sable timeout\n", - __func__, vsi->seid, pf_q, - (enable ? "en" : "dis")); + "VSI seid %d Rx ring %d %sable timeout\n", + vsi->seid, pf_q, (enable ? "en" : "dis")); break; } } @@ -4037,17 +4035,15 @@ static void i40e_quiesce_vsi(struct i40e_vsi *vsi) if ((test_bit(__I40E_PORT_TX_SUSPENDED, &vsi->back->state)) && vsi->type == I40E_VSI_FCOE) { dev_dbg(&vsi->back->pdev->dev, - "%s: VSI seid %d skipping FCoE VSI disable\n", - __func__, vsi->seid); + "VSI seid %d skipping FCoE VSI disable\n", vsi->seid); return; } set_bit(__I40E_NEEDS_RESTART, &vsi->state); - if (vsi->netdev && netif_running(vsi->netdev)) { + if (vsi->netdev && netif_running(vsi->netdev)) vsi->netdev->netdev_ops->ndo_stop(vsi->netdev); - } else { + else i40e_vsi_close(vsi); - } } /** @@ -4112,8 +4108,8 @@ static int i40e_vsi_wait_txq_disabled(struct i40e_vsi *vsi) ret = i40e_pf_txq_wait(pf, pf_q, false); if (ret) { dev_info(&pf->pdev->dev, - "%s: VSI seid %d Tx ring %d disable timeout\n", - __func__, vsi->seid, pf_q); + "VSI seid %d Tx ring %d disable timeout\n", + vsi->seid, pf_q); return ret; } } @@ -4145,6 +4141,108 @@ static int i40e_pf_wait_txq_disabled(struct i40e_pf *pf) } #endif + +/** + * i40e_detect_recover_hung_queue - Function to detect and recover hung_queue + * @q_idx: TX queue number + * @vsi: Pointer to VSI struct + * + * This function checks specified queue for given VSI. Detects hung condition. + * Sets hung bit since it is two step process. Before next run of service task + * if napi_poll runs, it reset 'hung' bit for respective q_vector. If not, + * hung condition remain unchanged and during subsequent run, this function + * issues SW interrupt to recover from hung condition. + **/ +static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi) +{ + struct i40e_ring *tx_ring = NULL; + struct i40e_pf *pf; + u32 head, val, tx_pending; + int i; + + pf = vsi->back; + + /* now that we have an index, find the tx_ring struct */ + for (i = 0; i < vsi->num_queue_pairs; i++) { + if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc) { + if (q_idx == vsi->tx_rings[i]->queue_index) { + tx_ring = vsi->tx_rings[i]; + break; + } + } + } + + if (!tx_ring) + return; + + /* Read interrupt register */ + if (pf->flags & I40E_FLAG_MSIX_ENABLED) + val = rd32(&pf->hw, + I40E_PFINT_DYN_CTLN(tx_ring->q_vector->v_idx + + tx_ring->vsi->base_vector - 1)); + else + val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0); + + head = i40e_get_head(tx_ring); + + tx_pending = i40e_get_tx_pending(tx_ring); + + /* Interrupts are disabled and TX pending is non-zero, + * trigger the SW interrupt (don't wait). Worst case + * there will be one extra interrupt which may result + * into not cleaning any queues because queues are cleaned. + */ + if (tx_pending && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) + i40e_force_wb(vsi, tx_ring->q_vector); +} + +/** + * i40e_detect_recover_hung - Function to detect and recover hung_queues + * @pf: pointer to PF struct + * + * LAN VSI has netdev and netdev has TX queues. This function is to check + * each of those TX queues if they are hung, trigger recovery by issuing + * SW interrupt. + **/ +static void i40e_detect_recover_hung(struct i40e_pf *pf) +{ + struct net_device *netdev; + struct i40e_vsi *vsi; + int i; + + /* Only for LAN VSI */ + vsi = pf->vsi[pf->lan_vsi]; + + if (!vsi) + return; + + /* Make sure, VSI state is not DOWN/RECOVERY_PENDING */ + if (test_bit(__I40E_DOWN, &vsi->back->state) || + test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) + return; + + /* Make sure type is MAIN VSI */ + if (vsi->type != I40E_VSI_MAIN) + return; + + netdev = vsi->netdev; + if (!netdev) + return; + + /* Bail out if netif_carrier is not OK */ + if (!netif_carrier_ok(netdev)) + return; + + /* Go thru' TX queues for netdev */ + for (i = 0; i < netdev->num_tx_queues; i++) { + struct netdev_queue *q; + + q = netdev_get_tx_queue(netdev, i); + if (q) + i40e_detect_recover_hung_queue(i, vsi); + } +} + /** * i40e_get_iscsi_tc_map - Return TC map for iSCSI APP * @pf: pointer to PF @@ -4744,11 +4842,14 @@ out: * i40e_print_link_message - print link up or down * @vsi: the VSI for which link needs a message */ -static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) +void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) { char speed[SPEED_SIZE] = "Unknown"; char fc[FC_SIZE] = "RX/TX"; + if (vsi->current_isup == isup) + return; + vsi->current_isup = isup; if (!isup) { netdev_info(vsi->netdev, "NIC Link is Down\n"); return; @@ -5217,15 +5318,13 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) "VSI reinit requested\n"); for (v = 0; v < pf->num_alloc_vsi; v++) { struct i40e_vsi *vsi = pf->vsi[v]; + if (vsi != NULL && test_bit(__I40E_REINIT_REQUESTED, &vsi->state)) { i40e_vsi_reinit_locked(pf->vsi[v]); clear_bit(__I40E_REINIT_REQUESTED, &vsi->state); } } - - /* no further action needed, so return now */ - return; } else if (reset_flags & BIT_ULL(__I40E_DOWN_REQUESTED)) { int v; @@ -5233,6 +5332,7 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) dev_info(&pf->pdev->dev, "VSI down requested\n"); for (v = 0; v < pf->num_alloc_vsi; v++) { struct i40e_vsi *vsi = pf->vsi[v]; + if (vsi != NULL && test_bit(__I40E_DOWN_REQUESTED, &vsi->state)) { set_bit(__I40E_DOWN, &vsi->state); @@ -5240,13 +5340,9 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) clear_bit(__I40E_DOWN_REQUESTED, &vsi->state); } } - - /* no further action needed, so return now */ - return; } else { dev_info(&pf->pdev->dev, "bad reset request 0x%08x\n", reset_flags); - return; } } @@ -5302,8 +5398,7 @@ bool i40e_dcb_need_reconfig(struct i40e_pf *pf, dev_dbg(&pf->pdev->dev, "APP Table change detected.\n"); } - dev_dbg(&pf->pdev->dev, "%s: need_reconfig=%d\n", __func__, - need_reconfig); + dev_dbg(&pf->pdev->dev, "dcb need_reconfig=%d\n", need_reconfig); return need_reconfig; } @@ -5330,16 +5425,14 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf, /* Ignore if event is not for Nearest Bridge */ type = ((mib->type >> I40E_AQ_LLDP_BRIDGE_TYPE_SHIFT) & I40E_AQ_LLDP_BRIDGE_TYPE_MASK); - dev_dbg(&pf->pdev->dev, - "%s: LLDP event mib bridge type 0x%x\n", __func__, type); + dev_dbg(&pf->pdev->dev, "LLDP event mib bridge type 0x%x\n", type); if (type != I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE) return ret; /* Check MIB Type and return if event for Remote MIB update */ type = mib->type & I40E_AQ_LLDP_MIB_TYPE_MASK; dev_dbg(&pf->pdev->dev, - "%s: LLDP event mib type %s\n", __func__, - type ? "remote" : "local"); + "LLDP event mib type %s\n", type ? "remote" : "local"); if (type == I40E_AQ_LLDP_MIB_REMOTE) { /* Update the remote cached instance and return */ ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE, @@ -5524,7 +5617,9 @@ u32 i40e_get_global_fd_count(struct i40e_pf *pf) **/ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) { + struct i40e_fdir_filter *filter; u32 fcnt_prog, fcnt_avail; + struct hlist_node *node; if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state)) return; @@ -5553,6 +5648,18 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table now\n"); } } + + /* if hw had a problem adding a filter, delete it */ + if (pf->fd_inv > 0) { + hlist_for_each_entry_safe(filter, node, + &pf->fdir_filter_list, fdir_node) { + if (filter->fd_id == pf->fd_inv) { + hlist_del(&filter->fdir_node); + kfree(filter); + pf->fdir_pf_active_filters--; + } + } + } } #define I40E_MIN_FD_FLUSH_INTERVAL 10 @@ -5572,49 +5679,51 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) if (!(pf->flags & (I40E_FLAG_FD_SB_ENABLED | I40E_FLAG_FD_ATR_ENABLED))) return; - if (time_after(jiffies, pf->fd_flush_timestamp + - (I40E_MIN_FD_FLUSH_INTERVAL * HZ))) { - /* If the flush is happening too quick and we have mostly - * SB rules we should not re-enable ATR for some time. - */ - min_flush_time = pf->fd_flush_timestamp - + (I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE * HZ); - fd_room = pf->fdir_pf_filter_count - pf->fdir_pf_active_filters; + if (!time_after(jiffies, pf->fd_flush_timestamp + + (I40E_MIN_FD_FLUSH_INTERVAL * HZ))) + return; - if (!(time_after(jiffies, min_flush_time)) && - (fd_room < I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) { - if (I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "ATR disabled, not enough FD filter space.\n"); - disable_atr = true; - } + /* If the flush is happening too quick and we have mostly SB rules we + * should not re-enable ATR for some time. + */ + min_flush_time = pf->fd_flush_timestamp + + (I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE * HZ); + fd_room = pf->fdir_pf_filter_count - pf->fdir_pf_active_filters; - pf->fd_flush_timestamp = jiffies; - pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED; - /* flush all filters */ - wr32(&pf->hw, I40E_PFQF_CTL_1, - I40E_PFQF_CTL_1_CLEARFDTABLE_MASK); - i40e_flush(&pf->hw); - pf->fd_flush_cnt++; - pf->fd_add_err = 0; - do { - /* Check FD flush status every 5-6msec */ - usleep_range(5000, 6000); - reg = rd32(&pf->hw, I40E_PFQF_CTL_1); - if (!(reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK)) - break; - } while (flush_wait_retry--); - if (reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK) { - dev_warn(&pf->pdev->dev, "FD table did not flush, needs more time\n"); - } else { - /* replay sideband filters */ - i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); - if (!disable_atr) - pf->flags |= I40E_FLAG_FD_ATR_ENABLED; - clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); - if (I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n"); - } + if (!(time_after(jiffies, min_flush_time)) && + (fd_room < I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) { + if (I40E_DEBUG_FD & pf->hw.debug_mask) + dev_info(&pf->pdev->dev, "ATR disabled, not enough FD filter space.\n"); + disable_atr = true; + } + + pf->fd_flush_timestamp = jiffies; + pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED; + /* flush all filters */ + wr32(&pf->hw, I40E_PFQF_CTL_1, + I40E_PFQF_CTL_1_CLEARFDTABLE_MASK); + i40e_flush(&pf->hw); + pf->fd_flush_cnt++; + pf->fd_add_err = 0; + do { + /* Check FD flush status every 5-6msec */ + usleep_range(5000, 6000); + reg = rd32(&pf->hw, I40E_PFQF_CTL_1); + if (!(reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK)) + break; + } while (flush_wait_retry--); + if (reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK) { + dev_warn(&pf->pdev->dev, "FD table did not flush, needs more time\n"); + } else { + /* replay sideband filters */ + i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); + if (!disable_atr) + pf->flags |= I40E_FLAG_FD_ATR_ENABLED; + clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); + if (I40E_DEBUG_FD & pf->hw.debug_mask) + dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n"); } + } /** @@ -5722,15 +5831,23 @@ static void i40e_veb_link_event(struct i40e_veb *veb, bool link_up) **/ static void i40e_link_event(struct i40e_pf *pf) { - bool new_link, old_link; struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; u8 new_link_speed, old_link_speed; + i40e_status status; + bool new_link, old_link; /* set this to force the get_link_status call to refresh state */ pf->hw.phy.get_link_info = true; old_link = (pf->hw.phy.link_info_old.link_info & I40E_AQ_LINK_UP); - new_link = i40e_get_link_status(&pf->hw); + + status = i40e_get_link_status(&pf->hw, &new_link); + if (status) { + dev_dbg(&pf->pdev->dev, "couldn't get link state, status: %d\n", + status); + return; + } + old_link_speed = pf->hw.phy.link_info_old.link_speed; new_link_speed = pf->hw.phy.link_info.link_speed; @@ -5759,68 +5876,6 @@ static void i40e_link_event(struct i40e_pf *pf) } /** - * i40e_check_hang_subtask - Check for hung queues and dropped interrupts - * @pf: board private structure - * - * Set the per-queue flags to request a check for stuck queues in the irq - * clean functions, then force interrupts to be sure the irq clean is called. - **/ -static void i40e_check_hang_subtask(struct i40e_pf *pf) -{ - int i, v; - - /* If we're down or resetting, just bail */ - if (test_bit(__I40E_DOWN, &pf->state) || - test_bit(__I40E_CONFIG_BUSY, &pf->state)) - return; - - /* for each VSI/netdev - * for each Tx queue - * set the check flag - * for each q_vector - * force an interrupt - */ - for (v = 0; v < pf->num_alloc_vsi; v++) { - struct i40e_vsi *vsi = pf->vsi[v]; - int armed = 0; - - if (!pf->vsi[v] || - test_bit(__I40E_DOWN, &vsi->state) || - (vsi->netdev && !netif_carrier_ok(vsi->netdev))) - continue; - - for (i = 0; i < vsi->num_queue_pairs; i++) { - set_check_for_tx_hang(vsi->tx_rings[i]); - if (test_bit(__I40E_HANG_CHECK_ARMED, - &vsi->tx_rings[i]->state)) - armed++; - } - - if (armed) { - if (!(pf->flags & I40E_FLAG_MSIX_ENABLED)) { - wr32(&vsi->back->hw, I40E_PFINT_DYN_CTL0, - (I40E_PFINT_DYN_CTL0_INTENA_MASK | - I40E_PFINT_DYN_CTL0_SWINT_TRIG_MASK | - I40E_PFINT_DYN_CTL0_ITR_INDX_MASK | - I40E_PFINT_DYN_CTL0_SW_ITR_INDX_ENA_MASK | - I40E_PFINT_DYN_CTL0_SW_ITR_INDX_MASK)); - } else { - u16 vec = vsi->base_vector - 1; - u32 val = (I40E_PFINT_DYN_CTLN_INTENA_MASK | - I40E_PFINT_DYN_CTLN_SWINT_TRIG_MASK | - I40E_PFINT_DYN_CTLN_ITR_INDX_MASK | - I40E_PFINT_DYN_CTLN_SW_ITR_INDX_ENA_MASK | - I40E_PFINT_DYN_CTLN_SW_ITR_INDX_MASK); - for (i = 0; i < vsi->num_q_vectors; i++, vec++) - wr32(&vsi->back->hw, - I40E_PFINT_DYN_CTLN(vec), val); - } - i40e_flush(&vsi->back->hw); - } - } -} - -/** * i40e_watchdog_subtask - periodic checks not using event driven response * @pf: board private structure **/ @@ -5839,8 +5894,8 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf) return; pf->service_timer_previous = jiffies; - i40e_check_hang_subtask(pf); - i40e_link_event(pf); + if (pf->flags & I40E_FLAG_LINK_POLLING_ENABLED) + i40e_link_event(pf); /* Update the stats for active netdevs so the network stack * can look at updated numbers whenever it cares to @@ -5849,10 +5904,12 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf) if (pf->vsi[i] && pf->vsi[i]->netdev) i40e_update_stats(pf->vsi[i]); - /* Update the stats for the active switching components */ - for (i = 0; i < I40E_MAX_VEB; i++) - if (pf->veb[i]) - i40e_update_veb_stats(pf->veb[i]); + if (pf->flags & I40E_FLAG_VEB_STATS_ENABLED) { + /* Update the stats for the active switching components */ + for (i = 0; i < I40E_MAX_VEB; i++) + if (pf->veb[i]) + i40e_update_veb_stats(pf->veb[i]); + } i40e_ptp_rx_hang(pf->vsi[pf->lan_vsi]); } @@ -6231,6 +6288,7 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb) if (pf->vsi[v]->veb_idx == veb->idx) { struct i40e_vsi *vsi = pf->vsi[v]; + vsi->uplink_seid = veb->seid; ret = i40e_add_vsi(vsi); if (ret) { @@ -6513,9 +6571,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) } #endif /* CONFIG_I40E_DCB */ #ifdef I40E_FCOE - ret = i40e_init_pf_fcoe(pf); - if (ret) - dev_info(&pf->pdev->dev, "init_pf_fcoe failed: %d\n", ret); + i40e_init_pf_fcoe(pf); #endif /* do basic switch setup */ @@ -6807,6 +6863,7 @@ static void i40e_service_task(struct work_struct *work) return; } + i40e_detect_recover_hung(pf); i40e_reset_subtask(pf); i40e_handle_mdd_event(pf); i40e_vc_process_vflr_event(pf); @@ -7565,7 +7622,7 @@ static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed) "Cannot set RSS key, err %s aq_err %s\n", i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - return ret; + goto config_rss_aq_out; } if (vsi->type == I40E_VSI_MAIN) @@ -7579,6 +7636,8 @@ static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed) i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); +config_rss_aq_out: + kfree(rss_lut); return ret; } @@ -7853,6 +7912,7 @@ static int i40e_sw_init(struct i40e_pf *pf) /* Set default capability flags */ pf->flags = I40E_FLAG_RX_CSUM_ENABLED | I40E_FLAG_MSI_ENABLED | + I40E_FLAG_LINK_POLLING_ENABLED | I40E_FLAG_MSIX_ENABLED; if (iommu_present(&pci_bus_type)) @@ -7913,9 +7973,7 @@ static int i40e_sw_init(struct i40e_pf *pf) } #ifdef I40E_FCOE - err = i40e_init_pf_fcoe(pf); - if (err) - dev_info(&pf->pdev->dev, "init_pf_fcoe failed: %d\n", err); + i40e_init_pf_fcoe(pf); #endif /* I40E_FCOE */ #ifdef CONFIG_PCI_IOV @@ -7939,6 +7997,9 @@ static int i40e_sw_init(struct i40e_pf *pf) pf->lan_veb = I40E_NO_VEB; pf->lan_vsi = I40E_NO_VSI; + /* By default FW has this off for performance reasons */ + pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED; + /* set up queue assignment tracking */ size = sizeof(struct i40e_lump_tracking) + (sizeof(u16) * pf->hw.func_caps.num_tx_qp); @@ -8118,9 +8179,6 @@ static void i40e_del_vxlan_port(struct net_device *netdev, pf->vxlan_ports[idx] = 0; pf->pending_vxlan_bitmap |= BIT_ULL(idx); pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC; - - dev_info(&pf->pdev->dev, "deleting vxlan port %d\n", - ntohs(port)); } else { netdev_warn(netdev, "vxlan port %d was not found, not deleting\n", ntohs(port)); @@ -8770,7 +8828,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi) list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) i40e_del_filter(vsi, f->macaddr, f->vlan, f->is_vf, f->is_netdev); - i40e_sync_vsi_filters(vsi); + i40e_sync_vsi_filters(vsi, false); i40e_vsi_delete(vsi); i40e_vsi_free_q_vectors(vsi); @@ -8995,8 +9053,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, if (veb) { if (vsi->seid != pf->vsi[pf->lan_vsi]->seid) { dev_info(&vsi->back->pdev->dev, - "%s: New VSI creation error, uplink seid of LAN VSI expected.\n", - __func__); + "New VSI creation error, uplink seid of LAN VSI expected.\n"); return NULL; } /* We come up by default in VEPA mode if SRIOV is not @@ -9646,6 +9703,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) } else { /* force a reset of TC and queue layout configurations */ u8 enabled_tc = pf->vsi[pf->lan_vsi]->tc_config.enabled_tc; + pf->vsi[pf->lan_vsi]->tc_config.enabled_tc = 0; pf->vsi[pf->lan_vsi]->seid = pf->main_vsi_seid; i40e_vsi_config_tc(pf->vsi[pf->lan_vsi], enabled_tc); @@ -9856,6 +9914,9 @@ static void i40e_print_features(struct i40e_pf *pf) } if (pf->flags & I40E_FLAG_DCB_CAPABLE) buf += sprintf(buf, "DCB "); +#if IS_ENABLED(CONFIG_VXLAN) + buf += sprintf(buf, "VxLAN "); +#endif if (pf->flags & I40E_FLAG_PTP) buf += sprintf(buf, "PTP "); #ifdef I40E_FCOE @@ -9882,10 +9943,10 @@ static void i40e_print_features(struct i40e_pf *pf) static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct i40e_aq_get_phy_abilities_resp abilities; - unsigned long ioremap_len; struct i40e_pf *pf; struct i40e_hw *hw; static u16 pfs_found; + u16 wol_nvm_bits; u16 link_status; int err = 0; u32 len; @@ -9935,15 +9996,15 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw = &pf->hw; hw->back = pf; - ioremap_len = min_t(unsigned long, pci_resource_len(pdev, 0), - I40E_MAX_CSR_SPACE); + pf->ioremap_len = min_t(int, pci_resource_len(pdev, 0), + I40E_MAX_CSR_SPACE); - hw->hw_addr = ioremap(pci_resource_start(pdev, 0), ioremap_len); + hw->hw_addr = ioremap(pci_resource_start(pdev, 0), pf->ioremap_len); if (!hw->hw_addr) { err = -EIO; dev_info(&pdev->dev, "ioremap(0x%04x, 0x%04x) failed: 0x%x\n", (unsigned int)pci_resource_start(pdev, 0), - (unsigned int)pci_resource_len(pdev, 0), err); + pf->ioremap_len, err); goto err_ioremap; } hw->vendor_id = pdev->vendor; @@ -10101,10 +10162,13 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) INIT_WORK(&pf->service_task, i40e_service_task); clear_bit(__I40E_SERVICE_SCHED, &pf->state); pf->flags |= I40E_FLAG_NEED_LINK_UPDATE; - pf->link_check_timeout = jiffies; - /* WoL defaults to disabled */ - pf->wol_en = false; + /* NVM bit on means WoL disabled for the port */ + i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits); + if ((1 << hw->port) & wol_nvm_bits || hw->partition_id != 1) + pf->wol_en = false; + else + pf->wol_en = true; device_set_wakeup_enable(&pf->pdev->dev, pf->wol_en); /* set up the main switch operations */ @@ -10436,7 +10500,7 @@ static pci_ers_result_t i40e_pci_error_slot_reset(struct pci_dev *pdev) int err; u32 reg; - dev_info(&pdev->dev, "%s\n", __func__); + dev_dbg(&pdev->dev, "%s\n", __func__); if (pci_enable_device_mem(pdev)) { dev_info(&pdev->dev, "Cannot re-enable PCI device after reset.\n"); @@ -10476,13 +10540,13 @@ static void i40e_pci_error_resume(struct pci_dev *pdev) { struct i40e_pf *pf = pci_get_drvdata(pdev); - dev_info(&pdev->dev, "%s\n", __func__); + dev_dbg(&pdev->dev, "%s\n", __func__); if (test_bit(__I40E_SUSPENDED, &pf->state)) return; rtnl_lock(); i40e_handle_reset_warning(pf); - rtnl_lock(); + rtnl_unlock(); } /** @@ -10568,9 +10632,7 @@ static int i40e_resume(struct pci_dev *pdev) err = pci_enable_device_mem(pdev); if (err) { - dev_err(&pdev->dev, - "%s: Cannot enable PCI device from suspend\n", - __func__); + dev_err(&pdev->dev, "Cannot enable PCI device from suspend\n"); return err; } pci_set_master(pdev); diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 9b83abc0e774..2142e1004a2f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -418,6 +418,10 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, bool last_command) { i40e_status ret_code = I40E_ERR_NVM; + struct i40e_asq_cmd_details cmd_details; + + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; /* Here we are checking the SR limit only for the flat memory model. * We cannot do it for the module-based model, as we did not acquire @@ -443,7 +447,7 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, ret_code = i40e_aq_update_nvm(hw, module_pointer, 2 * offset, /*bytes*/ 2 * words, /*bytes*/ - data, last_command, NULL); + data, last_command, &cmd_details); return ret_code; } @@ -543,11 +547,13 @@ i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw) { i40e_status ret_code = 0; u16 checksum; + __le16 le_sum; ret_code = i40e_calc_nvm_checksum(hw, &checksum); + le_sum = cpu_to_le16(checksum); if (!ret_code) ret_code = i40e_write_nvm_aq(hw, 0x00, I40E_SR_SW_CHECKSUM_WORD, - 1, &checksum, true); + 1, &le_sum, true); return ret_code; } @@ -592,25 +598,31 @@ i40e_validate_nvm_checksum_exit: static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, struct i40e_nvm_access *cmd, u8 *bytes, int *errno); static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno); + int *perrno); static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno); + int *perrno); static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); +static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); static inline u8 i40e_nvmupd_get_module(u32 val) { return (u8)(val & I40E_NVM_MOD_PNT_MASK); @@ -634,6 +646,9 @@ static char *i40e_nvm_update_state_str[] = { "I40E_NVMUPD_CSUM_CON", "I40E_NVMUPD_CSUM_SA", "I40E_NVMUPD_CSUM_LCB", + "I40E_NVMUPD_STATUS", + "I40E_NVMUPD_EXEC_AQ", + "I40E_NVMUPD_GET_AQ_RESULT", }; /** @@ -641,30 +656,60 @@ static char *i40e_nvm_update_state_str[] = { * @hw: pointer to hardware structure * @cmd: pointer to nvm update command * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * Dispatches command depending on what update state is current **/ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { i40e_status status; + enum i40e_nvmupd_cmd upd_cmd; /* assume success */ - *errno = 0; + *perrno = 0; + + /* early check for status command and debug msgs */ + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); + + i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n", + i40e_nvm_update_state_str[upd_cmd], + hw->nvmupd_state, + hw->aq.nvm_release_on_done); + + if (upd_cmd == I40E_NVMUPD_INVALID) { + *perrno = -EFAULT; + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_validate_command returns %d errno %d\n", + upd_cmd, *perrno); + } + + /* a status request returns immediately rather than + * going into the state machine + */ + if (upd_cmd == I40E_NVMUPD_STATUS) { + bytes[0] = hw->nvmupd_state; + return 0; + } switch (hw->nvmupd_state) { case I40E_NVMUPD_STATE_INIT: - status = i40e_nvmupd_state_init(hw, cmd, bytes, errno); + status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno); break; case I40E_NVMUPD_STATE_READING: - status = i40e_nvmupd_state_reading(hw, cmd, bytes, errno); + status = i40e_nvmupd_state_reading(hw, cmd, bytes, perrno); break; case I40E_NVMUPD_STATE_WRITING: - status = i40e_nvmupd_state_writing(hw, cmd, bytes, errno); + status = i40e_nvmupd_state_writing(hw, cmd, bytes, perrno); + break; + + case I40E_NVMUPD_STATE_INIT_WAIT: + case I40E_NVMUPD_STATE_WRITE_WAIT: + status = I40E_ERR_NOT_READY; + *perrno = -EBUSY; break; default: @@ -672,7 +717,7 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: no such state %d\n", hw->nvmupd_state); status = I40E_NOT_SUPPORTED; - *errno = -ESRCH; + *perrno = -ESRCH; break; } return status; @@ -683,28 +728,28 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * Process legitimate commands of the Init state and conditionally set next * state. Reject all other commands. **/ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { i40e_status status = 0; enum i40e_nvmupd_cmd upd_cmd; - upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); switch (upd_cmd) { case I40E_NVMUPD_READ_SA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); i40e_release_nvm(hw); } break; @@ -712,10 +757,10 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, case I40E_NVMUPD_READ_SNT: status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); if (status) i40e_release_nvm(hw); else @@ -726,70 +771,83 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, case I40E_NVMUPD_WRITE_ERA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_erase(hw, cmd, errno); - if (status) + status = i40e_nvmupd_nvm_erase(hw, cmd, perrno); + if (status) { i40e_release_nvm(hw); - else + } else { hw->aq.nvm_release_on_done = true; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } } break; case I40E_NVMUPD_WRITE_SA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); - if (status) + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (status) { i40e_release_nvm(hw); - else + } else { hw->aq.nvm_release_on_done = true; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } } break; case I40E_NVMUPD_WRITE_SNT: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); if (status) i40e_release_nvm(hw); else - hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING; + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; } break; case I40E_NVMUPD_CSUM_SA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { status = i40e_update_nvm_checksum(hw); if (status) { - *errno = hw->aq.asq_last_status ? + *perrno = hw->aq.asq_last_status ? i40e_aq_rc_to_posix(status, hw->aq.asq_last_status) : -EIO; i40e_release_nvm(hw); } else { hw->aq.nvm_release_on_done = true; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; } } break; + case I40E_NVMUPD_EXEC_AQ: + status = i40e_nvmupd_exec_aq(hw, cmd, bytes, perrno); + break; + + case I40E_NVMUPD_GET_AQ_RESULT: + status = i40e_nvmupd_get_aq_result(hw, cmd, bytes, perrno); + break; + default: i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: bad cmd %s in init state\n", i40e_nvm_update_state_str[upd_cmd]); status = I40E_ERR_NVM; - *errno = -ESRCH; + *perrno = -ESRCH; break; } return status; @@ -800,28 +858,28 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * NVM ownership is already held. Process legitimate commands and set any * change in state; reject all other commands. **/ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { - i40e_status status; + i40e_status status = 0; enum i40e_nvmupd_cmd upd_cmd; - upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); switch (upd_cmd) { case I40E_NVMUPD_READ_SA: case I40E_NVMUPD_READ_CON: - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); break; case I40E_NVMUPD_READ_LCB: - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); i40e_release_nvm(hw); hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; break; @@ -831,7 +889,7 @@ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, "NVMUPD: bad cmd %s in reading state.\n", i40e_nvm_update_state_str[upd_cmd]); status = I40E_NOT_SUPPORTED; - *errno = -ESRCH; + *perrno = -ESRCH; break; } return status; @@ -842,55 +900,68 @@ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * NVM ownership is already held. Process legitimate commands and set any * change in state; reject all other commands **/ static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { - i40e_status status; + i40e_status status = 0; enum i40e_nvmupd_cmd upd_cmd; bool retry_attempt = false; - upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); retry: switch (upd_cmd) { case I40E_NVMUPD_WRITE_CON: - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (!status) + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; break; case I40E_NVMUPD_WRITE_LCB: - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); - if (!status) + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (status) { + *perrno = hw->aq.asq_last_status ? + i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status) : + -EIO; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { hw->aq.nvm_release_on_done = true; - hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } break; case I40E_NVMUPD_CSUM_CON: status = i40e_update_nvm_checksum(hw); if (status) { - *errno = hw->aq.asq_last_status ? + *perrno = hw->aq.asq_last_status ? i40e_aq_rc_to_posix(status, hw->aq.asq_last_status) : -EIO; hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; } break; case I40E_NVMUPD_CSUM_LCB: status = i40e_update_nvm_checksum(hw); - if (status) - *errno = hw->aq.asq_last_status ? + if (status) { + *perrno = hw->aq.asq_last_status ? i40e_aq_rc_to_posix(status, hw->aq.asq_last_status) : -EIO; - else + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { hw->aq.nvm_release_on_done = true; - hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } break; default: @@ -898,7 +969,7 @@ retry: "NVMUPD: bad cmd %s in writing state.\n", i40e_nvm_update_state_str[upd_cmd]); status = I40E_NOT_SUPPORTED; - *errno = -ESRCH; + *perrno = -ESRCH; break; } @@ -941,21 +1012,22 @@ retry: * i40e_nvmupd_validate_command - Validate given command * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * Return one of the valid command types or I40E_NVMUPD_INVALID **/ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno) + int *perrno) { enum i40e_nvmupd_cmd upd_cmd; - u8 transaction; + u8 module, transaction; /* anything that doesn't match a recognized case is an error */ upd_cmd = I40E_NVMUPD_INVALID; transaction = i40e_nvmupd_get_transaction(cmd->config); + module = i40e_nvmupd_get_module(cmd->config); /* limits on data size */ if ((cmd->data_size < 1) || @@ -963,7 +1035,7 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_validate_command data_size %d\n", cmd->data_size); - *errno = -EFAULT; + *perrno = -EFAULT; return I40E_NVMUPD_INVALID; } @@ -982,6 +1054,12 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, case I40E_NVM_SA: upd_cmd = I40E_NVMUPD_READ_SA; break; + case I40E_NVM_EXEC: + if (module == 0xf) + upd_cmd = I40E_NVMUPD_STATUS; + else if (module == 0) + upd_cmd = I40E_NVMUPD_GET_AQ_RESULT; + break; } break; @@ -1011,21 +1089,155 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, case (I40E_NVM_CSUM|I40E_NVM_LCB): upd_cmd = I40E_NVMUPD_CSUM_LCB; break; + case I40E_NVM_EXEC: + if (module == 0) + upd_cmd = I40E_NVMUPD_EXEC_AQ; + break; } break; } - i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n", - i40e_nvm_update_state_str[upd_cmd], - hw->nvmupd_state, - hw->aq.nvm_release_on_done); - if (upd_cmd == I40E_NVMUPD_INVALID) { - *errno = -EFAULT; + return upd_cmd; +} + +/** + * i40e_nvmupd_exec_aq - Run an AQ command + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * cmd structure contains identifiers and data buffer + **/ +static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + struct i40e_asq_cmd_details cmd_details; + i40e_status status; + struct i40e_aq_desc *aq_desc; + u32 buff_size = 0; + u8 *buff = NULL; + u32 aq_desc_len; + u32 aq_data_len; + + i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + + aq_desc_len = sizeof(struct i40e_aq_desc); + memset(&hw->nvm_wb_desc, 0, aq_desc_len); + + /* get the aq descriptor */ + if (cmd->data_size < aq_desc_len) { i40e_debug(hw, I40E_DEBUG_NVM, - "i40e_nvmupd_validate_command returns %d errno %d\n", - upd_cmd, *errno); + "NVMUPD: not enough aq desc bytes for exec, size %d < %d\n", + cmd->data_size, aq_desc_len); + *perrno = -EINVAL; + return I40E_ERR_PARAM; } - return upd_cmd; + aq_desc = (struct i40e_aq_desc *)bytes; + + /* if data buffer needed, make sure it's ready */ + aq_data_len = cmd->data_size - aq_desc_len; + buff_size = max_t(u32, aq_data_len, le16_to_cpu(aq_desc->datalen)); + if (buff_size) { + if (!hw->nvm_buff.va) { + status = i40e_allocate_virt_mem(hw, &hw->nvm_buff, + hw->aq.asq_buf_size); + if (status) + i40e_debug(hw, I40E_DEBUG_NVM, + "NVMUPD: i40e_allocate_virt_mem for exec buff failed, %d\n", + status); + } + + if (hw->nvm_buff.va) { + buff = hw->nvm_buff.va; + memcpy(buff, &bytes[aq_desc_len], aq_data_len); + } + } + + /* and away we go! */ + status = i40e_asq_send_command(hw, aq_desc, buff, + buff_size, &cmd_details); + if (status) { + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_exec_aq err %s aq_err %s\n", + i40e_stat_str(hw, status), + i40e_aq_str(hw, hw->aq.asq_last_status)); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + } + + return status; +} + +/** + * i40e_nvmupd_get_aq_result - Get the results from the previous exec_aq + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * cmd structure contains identifiers and data buffer + **/ +static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + u32 aq_total_len; + u32 aq_desc_len; + int remainder; + u8 *buff; + + i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); + + aq_desc_len = sizeof(struct i40e_aq_desc); + aq_total_len = aq_desc_len + le16_to_cpu(hw->nvm_wb_desc.datalen); + + /* check offset range */ + if (cmd->offset > aq_total_len) { + i40e_debug(hw, I40E_DEBUG_NVM, "%s: offset too big %d > %d\n", + __func__, cmd->offset, aq_total_len); + *perrno = -EINVAL; + return I40E_ERR_PARAM; + } + + /* check copylength range */ + if (cmd->data_size > (aq_total_len - cmd->offset)) { + int new_len = aq_total_len - cmd->offset; + + i40e_debug(hw, I40E_DEBUG_NVM, "%s: copy length %d too big, trimming to %d\n", + __func__, cmd->data_size, new_len); + cmd->data_size = new_len; + } + + remainder = cmd->data_size; + if (cmd->offset < aq_desc_len) { + u32 len = aq_desc_len - cmd->offset; + + len = min(len, cmd->data_size); + i40e_debug(hw, I40E_DEBUG_NVM, "%s: aq_desc bytes %d to %d\n", + __func__, cmd->offset, cmd->offset + len); + + buff = ((u8 *)&hw->nvm_wb_desc) + cmd->offset; + memcpy(bytes, buff, len); + + bytes += len; + remainder -= len; + buff = hw->nvm_buff.va; + } else { + buff = hw->nvm_buff.va + (cmd->offset - aq_desc_len); + } + + if (remainder > 0) { + int start_byte = buff - (u8 *)hw->nvm_buff.va; + + i40e_debug(hw, I40E_DEBUG_NVM, "%s: databuf bytes %d to %d\n", + __func__, start_byte, start_byte + remainder); + memcpy(bytes, buff, remainder); + } + + return 0; } /** @@ -1033,14 +1245,15 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * cmd structure contains identifiers and data buffer **/ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { + struct i40e_asq_cmd_details cmd_details; i40e_status status; u8 module, transaction; bool last; @@ -1049,8 +1262,11 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, module = i40e_nvmupd_get_module(cmd->config); last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA); + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size, - bytes, last, NULL); + bytes, last, &cmd_details); if (status) { i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_read mod 0x%x off 0x%x len 0x%x\n", @@ -1058,7 +1274,7 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_read status %d aq %d\n", status, hw->aq.asq_last_status); - *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } return status; @@ -1068,23 +1284,28 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, * i40e_nvmupd_nvm_erase - Erase an NVM module * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * module, offset, data_size and data are in cmd structure **/ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno) + int *perrno) { i40e_status status = 0; + struct i40e_asq_cmd_details cmd_details; u8 module, transaction; bool last; transaction = i40e_nvmupd_get_transaction(cmd->config); module = i40e_nvmupd_get_module(cmd->config); last = (transaction & I40E_NVM_LCB); + + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size, - last, NULL); + last, &cmd_details); if (status) { i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_erase mod 0x%x off 0x%x len 0x%x\n", @@ -1092,7 +1313,7 @@ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_erase status %d aq %d\n", status, hw->aq.asq_last_status); - *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } return status; @@ -1103,15 +1324,16 @@ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * module, offset, data_size and data are in cmd structure **/ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { i40e_status status = 0; + struct i40e_asq_cmd_details cmd_details; u8 module, transaction; bool last; @@ -1119,8 +1341,12 @@ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, module = i40e_nvmupd_get_module(cmd->config); last = (transaction & I40E_NVM_LCB); + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + status = i40e_aq_update_nvm(hw, module, cmd->offset, - (u16)cmd->data_size, bytes, last, NULL); + (u16)cmd->data_size, bytes, last, + &cmd_details); if (status) { i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n", @@ -1128,7 +1354,7 @@ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_write status %d aq %d\n", status, hw->aq.asq_last_status); - *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } return status; diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index dcb72a8ee8e5..e51e1567837c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -258,7 +258,7 @@ i40e_status i40e_init_shared_code(struct i40e_hw *hw); i40e_status i40e_pf_reset(struct i40e_hw *hw); void i40e_clear_hw(struct i40e_hw *hw); void i40e_clear_pxe_mode(struct i40e_hw *hw); -bool i40e_get_link_status(struct i40e_hw *hw); +i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up); i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr); i40e_status i40e_read_bw_from_alt_ram(struct i40e_hw *hw, u32 *max_bw, u32 *min_bw, bool *min_valid, diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 8c40d6ea15fd..552c84e2e05d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -618,9 +618,8 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf) /* Attempt to register the clock before enabling the hardware. */ pf->ptp_clock = ptp_clock_register(&pf->ptp_caps, &pf->pdev->dev); - if (IS_ERR(pf->ptp_clock)) { + if (IS_ERR(pf->ptp_clock)) return PTR_ERR(pf->ptp_clock); - } /* clear the hwtstamp settings here during clock create, instead of * during regular init, so that we can maintain settings across a diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 738aca68f665..01e5ece8046d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -465,10 +465,11 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, I40E_RX_PROG_STATUS_DESC_QW1_ERROR_SHIFT; if (error == BIT(I40E_RX_PROG_STATUS_DESC_FD_TBL_FULL_SHIFT)) { + pf->fd_inv = le32_to_cpu(rx_desc->wb.qword0.hi_dword.fd_id); if ((rx_desc->wb.qword0.hi_dword.fd_id != 0) || (I40E_DEBUG_FD & pf->hw.debug_mask)) dev_warn(&pdev->dev, "ntuple filter loc = %d, could not be added\n", - rx_desc->wb.qword0.hi_dword.fd_id); + pf->fd_inv); /* Check if the programming error is for ATR. * If so, auto disable ATR and set a state for @@ -601,27 +602,13 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring) } /** - * i40e_get_head - Retrieve head from head writeback - * @tx_ring: tx ring to fetch head of - * - * Returns value of Tx ring head based on value stored - * in head write-back location - **/ -static inline u32 i40e_get_head(struct i40e_ring *tx_ring) -{ - void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count; - - return le32_to_cpu(*(volatile __le32 *)head); -} - -/** * i40e_get_tx_pending - how many tx descriptors not processed * @tx_ring: the ring of descriptors * * Since there is no access to the ring head register * in XL710, we need to use our local copies **/ -static u32 i40e_get_tx_pending(struct i40e_ring *ring) +u32 i40e_get_tx_pending(struct i40e_ring *ring) { u32 head, tail; @@ -635,50 +622,6 @@ static u32 i40e_get_tx_pending(struct i40e_ring *ring) return 0; } -/** - * i40e_check_tx_hang - Is there a hang in the Tx queue - * @tx_ring: the ring of descriptors - **/ -static bool i40e_check_tx_hang(struct i40e_ring *tx_ring) -{ - u32 tx_done = tx_ring->stats.packets; - u32 tx_done_old = tx_ring->tx_stats.tx_done_old; - u32 tx_pending = i40e_get_tx_pending(tx_ring); - struct i40e_pf *pf = tx_ring->vsi->back; - bool ret = false; - - clear_check_for_tx_hang(tx_ring); - - /* Check for a hung queue, but be thorough. This verifies - * that a transmit has been completed since the previous - * check AND there is at least one packet pending. The - * ARMED bit is set to indicate a potential hang. The - * bit is cleared if a pause frame is received to remove - * false hang detection due to PFC or 802.3x frames. By - * requiring this to fail twice we avoid races with - * PFC clearing the ARMED bit and conditions where we - * run the check_tx_hang logic with a transmit completion - * pending but without time to complete it yet. - */ - if ((tx_done_old == tx_done) && tx_pending) { - /* make sure it is true for two checks in a row */ - ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED, - &tx_ring->state); - } else if (tx_done_old == tx_done && - (tx_pending < I40E_MIN_DESC_PENDING) && (tx_pending > 0)) { - if (I40E_DEBUG_FLOW & pf->hw.debug_mask) - dev_info(tx_ring->dev, "HW needs some more descs to do a cacheline flush. tx_pending %d, queue %d", - tx_pending, tx_ring->queue_index); - pf->tx_sluggish_count++; - } else { - /* update completed stats and disarm the hang check */ - tx_ring->tx_stats.tx_done_old = tx_done; - clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state); - } - - return ret; -} - #define WB_STRIDE 0x3 /** @@ -784,42 +727,21 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) tx_ring->q_vector->tx.total_bytes += total_bytes; tx_ring->q_vector->tx.total_packets += total_packets; - /* check to see if there are any non-cache aligned descriptors - * waiting to be written back, and kick the hardware to force - * them to be written back in case of napi polling - */ - if (budget && - !((i & WB_STRIDE) == WB_STRIDE) && - !test_bit(__I40E_DOWN, &tx_ring->vsi->state) && - (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) - tx_ring->arm_wb = true; - else - tx_ring->arm_wb = false; - - if (check_for_tx_hang(tx_ring) && i40e_check_tx_hang(tx_ring)) { - /* schedule immediate reset if we believe we hung */ - dev_info(tx_ring->dev, "Detected Tx Unit Hang\n" - " VSI <%d>\n" - " Tx Queue <%d>\n" - " next_to_use <%x>\n" - " next_to_clean <%x>\n", - tx_ring->vsi->seid, - tx_ring->queue_index, - tx_ring->next_to_use, i); - - netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index); - - dev_info(tx_ring->dev, - "tx hang detected on queue %d, reset requested\n", - tx_ring->queue_index); - - /* do not fire the reset immediately, wait for the stack to - * decide we are truly stuck, also prevents every queue from - * simultaneously requesting a reset + if (tx_ring->flags & I40E_TXR_FLAGS_WB_ON_ITR) { + unsigned int j = 0; + + /* check to see if there are < 4 descriptors + * waiting to be written back, then kick the hardware to force + * them to be written back in case we stay in NAPI. + * In this mode on X722 we do not enable Interrupt. */ + j = i40e_get_tx_pending(tx_ring); - /* the adapter is about to reset, no point in enabling polling */ - budget = 1; + if (budget && + ((j / (WB_STRIDE + 1)) == 0) && (j != 0) && + !test_bit(__I40E_DOWN, &tx_ring->vsi->state) && + (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) + tx_ring->arm_wb = true; } netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev, @@ -851,7 +773,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) * @q_vector: the vector on which to force writeback * **/ -static void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) +void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) { u16 flags = q_vector->tx.ring[0].flags; @@ -1002,6 +924,8 @@ int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring) if (!dev) return -ENOMEM; + /* warn if we are about to overwrite the pointer */ + WARN_ON(tx_ring->tx_bi); bi_size = sizeof(struct i40e_tx_buffer) * tx_ring->count; tx_ring->tx_bi = kzalloc(bi_size, GFP_KERNEL); if (!tx_ring->tx_bi) @@ -1162,6 +1086,8 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring) struct device *dev = rx_ring->dev; int bi_size; + /* warn if we are about to overwrite the pointer */ + WARN_ON(rx_ring->rx_bi); bi_size = sizeof(struct i40e_rx_buffer) * rx_ring->count; rx_ring->rx_bi = kzalloc(bi_size, GFP_KERNEL); if (!rx_ring->rx_bi) @@ -1518,7 +1444,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) unsigned int total_rx_bytes = 0, total_rx_packets = 0; u16 rx_packet_len, rx_header_len, rx_sph, rx_hbo; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); - const int current_node = numa_node_id(); + const int current_node = numa_mem_id(); struct i40e_vsi *vsi = rx_ring->vsi; u16 i = rx_ring->next_to_clean; union i40e_rx_desc *rx_desc; @@ -1596,6 +1522,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) cleaned_count++; if (rx_hbo || rx_sph) { int len; + if (rx_hbo) len = I40E_RX_HDR_SIZE; else @@ -1781,9 +1708,6 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) /* ERR_MASK will only have valid bits if EOP set */ if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) { dev_kfree_skb_any(skb); - /* TODO: shouldn't we increment a counter indicating the - * drop? - */ continue; } @@ -1862,8 +1786,7 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, if (!test_bit(__I40E_DOWN, &vsi->state)) wr32(hw, I40E_PFINT_DYN_CTLN(vector - 1), val); } else { - i40e_irq_dynamic_enable(vsi, - q_vector->v_idx + vsi->base_vector); + i40e_irq_dynamic_enable(vsi, q_vector->v_idx); } if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) { old_itr = q_vector->tx.itr; @@ -1885,8 +1808,7 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, wr32(hw, I40E_PFINT_DYN_CTLN(q_vector->v_idx + vsi->base_vector - 1), val); } else { - i40e_irq_dynamic_enable(vsi, - q_vector->v_idx + vsi->base_vector); + i40e_irq_dynamic_enable(vsi, q_vector->v_idx); } } @@ -1921,6 +1843,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) i40e_for_each_ring(ring, q_vector->tx) { clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit); arm_wb |= ring->arm_wb; + ring->arm_wb = false; } /* We attempt to distribute budget to each Rx queue fairly, but don't @@ -2156,6 +2079,7 @@ static inline int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, /* else if it is a SW VLAN, check the next protocol and store the tag */ } else if (protocol == htons(ETH_P_8021Q)) { struct vlan_hdr *vhdr, _vhdr; + vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), &_vhdr); if (!vhdr) return -EINVAL; @@ -2324,6 +2248,9 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, l4_tunnel = I40E_TXD_CTX_UDP_TUNNELING; *tx_flags |= I40E_TX_FLAGS_VXLAN_TUNNEL; break; + case IPPROTO_GRE: + l4_tunnel = I40E_TXD_CTX_GRE_TUNNELING; + break; default: return; } @@ -2581,6 +2508,9 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, u32 td_tag = 0; dma_addr_t dma; u16 gso_segs; + u16 desc_count = 0; + bool tail_bump = true; + bool do_rs = false; if (tx_flags & I40E_TX_FLAGS_HW_VLAN) { td_cmd |= I40E_TX_DESC_CMD_IL2TAG1; @@ -2621,6 +2551,8 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_desc++; i++; + desc_count++; + if (i == tx_ring->count) { tx_desc = I40E_TX_DESC(tx_ring, 0); i = 0; @@ -2640,6 +2572,8 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_desc++; i++; + desc_count++; + if (i == tx_ring->count) { tx_desc = I40E_TX_DESC(tx_ring, 0); i = 0; @@ -2654,34 +2588,6 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_bi = &tx_ring->tx_bi[i]; } - /* Place RS bit on last descriptor of any packet that spans across the - * 4th descriptor (WB_STRIDE aka 0x3) in a 64B cacheline. - */ - if (((i & WB_STRIDE) != WB_STRIDE) && - (first <= &tx_ring->tx_bi[i]) && - (first >= &tx_ring->tx_bi[i & ~WB_STRIDE])) { - tx_desc->cmd_type_offset_bsz = - build_ctob(td_cmd, td_offset, size, td_tag) | - cpu_to_le64((u64)I40E_TX_DESC_CMD_EOP << - I40E_TXD_QW1_CMD_SHIFT); - } else { - tx_desc->cmd_type_offset_bsz = - build_ctob(td_cmd, td_offset, size, td_tag) | - cpu_to_le64((u64)I40E_TXD_CMD << - I40E_TXD_QW1_CMD_SHIFT); - } - - netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev, - tx_ring->queue_index), - first->bytecount); - - /* Force memory writes to complete before letting h/w - * know there are new descriptors to fetch. (Only - * applicable for weak-ordered memory model archs, - * such as IA-64). - */ - wmb(); - /* set next_to_watch value indicating a packet is present */ first->next_to_watch = tx_desc; @@ -2691,15 +2597,72 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_ring->next_to_use = i; + netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index), + first->bytecount); i40e_maybe_stop_tx(tx_ring, DESC_NEEDED); + + /* Algorithm to optimize tail and RS bit setting: + * if xmit_more is supported + * if xmit_more is true + * do not update tail and do not mark RS bit. + * if xmit_more is false and last xmit_more was false + * if every packet spanned less than 4 desc + * then set RS bit on 4th packet and update tail + * on every packet + * else + * update tail and set RS bit on every packet. + * if xmit_more is false and last_xmit_more was true + * update tail and set RS bit. + * + * Optimization: wmb to be issued only in case of tail update. + * Also optimize the Descriptor WB path for RS bit with the same + * algorithm. + * + * Note: If there are less than 4 packets + * pending and interrupts were disabled the service task will + * trigger a force WB. + */ + if (skb->xmit_more && + !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index))) { + tx_ring->flags |= I40E_TXR_FLAGS_LAST_XMIT_MORE_SET; + tail_bump = false; + } else if (!skb->xmit_more && + !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index)) && + (!(tx_ring->flags & I40E_TXR_FLAGS_LAST_XMIT_MORE_SET)) && + (tx_ring->packet_stride < WB_STRIDE) && + (desc_count < WB_STRIDE)) { + tx_ring->packet_stride++; + } else { + tx_ring->packet_stride = 0; + tx_ring->flags &= ~I40E_TXR_FLAGS_LAST_XMIT_MORE_SET; + do_rs = true; + } + if (do_rs) + tx_ring->packet_stride = 0; + + tx_desc->cmd_type_offset_bsz = + build_ctob(td_cmd, td_offset, size, td_tag) | + cpu_to_le64((u64)(do_rs ? I40E_TXD_CMD : + I40E_TX_DESC_CMD_EOP) << + I40E_TXD_QW1_CMD_SHIFT); + /* notify HW of packet */ - if (!skb->xmit_more || - netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev, - tx_ring->queue_index))) - writel(i, tx_ring->tail); - else + if (!tail_bump) prefetchw(tx_desc + 1); + if (tail_bump) { + /* Force memory writes to complete before letting h/w + * know there are new descriptors to fetch. (Only + * applicable for weak-ordered memory model archs, + * such as IA-64). + */ + wmb(); + writel(i, tx_ring->tail); + } + return; dma_error: @@ -2776,6 +2739,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, u8 hdr_len = 0; int tsyn; int tso; + if (0 == i40e_xmit_descriptor_count(skb, tx_ring)) return NETDEV_TX_BUSY; @@ -2808,10 +2772,11 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, if (tsyn) tx_flags |= I40E_TX_FLAGS_TSYN; - if (i40e_chk_linearize(skb, tx_flags)) + if (i40e_chk_linearize(skb, tx_flags)) { if (skb_linearize(skb)) goto out_drop; - + tx_ring->tx_stats.tx_linearize++; + } skb_tx_timestamp(skb); /* always enable CRC insertion offload */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index f1385a1989fa..75cecfa6e338 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -79,12 +79,12 @@ enum i40e_dyn_idx_t { BIT_ULL(I40E_FILTER_PCTYPE_L2_PAYLOAD)) #define I40E_DEFAULT_RSS_HENA_EXPANDED (I40E_DEFAULT_RSS_HENA | \ - BIT(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK) | \ - BIT(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \ - BIT(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \ - BIT(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK) | \ - BIT(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \ - BIT(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP)) + BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK) | \ + BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \ + BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \ + BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK) | \ + BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \ + BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP)) #define i40e_pf_get_default_rss_hena(pf) \ (((pf)->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \ @@ -165,6 +165,7 @@ struct i40e_tx_buffer { }; unsigned int bytecount; unsigned short gso_segs; + DEFINE_DMA_UNMAP_ADDR(dma); DEFINE_DMA_UNMAP_LEN(len); u32 tx_flags; @@ -188,6 +189,7 @@ struct i40e_tx_queue_stats { u64 restart_queue; u64 tx_busy; u64 tx_done_old; + u64 tx_linearize; }; struct i40e_rx_queue_stats { @@ -199,8 +201,6 @@ struct i40e_rx_queue_stats { enum i40e_ring_state_t { __I40E_TX_FDIR_INIT_DONE, __I40E_TX_XPS_INIT_DONE, - __I40E_TX_DETECT_HANG, - __I40E_HANG_CHECK_ARMED, __I40E_RX_PS_ENABLED, __I40E_RX_16BYTE_DESC_ENABLED, }; @@ -211,12 +211,6 @@ enum i40e_ring_state_t { set_bit(__I40E_RX_PS_ENABLED, &(ring)->state) #define clear_ring_ps_enabled(ring) \ clear_bit(__I40E_RX_PS_ENABLED, &(ring)->state) -#define check_for_tx_hang(ring) \ - test_bit(__I40E_TX_DETECT_HANG, &(ring)->state) -#define set_check_for_tx_hang(ring) \ - set_bit(__I40E_TX_DETECT_HANG, &(ring)->state) -#define clear_check_for_tx_hang(ring) \ - clear_bit(__I40E_TX_DETECT_HANG, &(ring)->state) #define ring_is_16byte_desc_enabled(ring) \ test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state) #define set_ring_16byte_desc_enabled(ring) \ @@ -264,10 +258,12 @@ struct i40e_ring { bool ring_active; /* is ring online or not */ bool arm_wb; /* do something to arm write back */ + u8 packet_stride; u16 flags; #define I40E_TXR_FLAGS_WB_ON_ITR BIT(0) #define I40E_TXR_FLAGS_OUTER_UDP_CSUM BIT(1) +#define I40E_TXR_FLAGS_LAST_XMIT_MORE_SET BIT(2) /* stats structs */ struct i40e_queue_stats stats; @@ -326,4 +322,20 @@ int i40e_xmit_descriptor_count(struct sk_buff *skb, struct i40e_ring *tx_ring); int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, struct i40e_ring *tx_ring, u32 *flags); #endif +void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector); +u32 i40e_get_tx_pending(struct i40e_ring *ring); + +/** + * i40e_get_head - Retrieve head from head writeback + * @tx_ring: tx ring to fetch head of + * + * Returns value of Tx ring head based on value stored + * in head write-back location + **/ +static inline u32 i40e_get_head(struct i40e_ring *tx_ring) +{ + void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count; + + return le32_to_cpu(*(volatile __le32 *)head); +} #endif /* _I40E_TXRX_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 4842239ee777..d1ec5a4326cf 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -45,6 +45,8 @@ #define I40E_DEV_ID_QSFP_C 0x1585 #define I40E_DEV_ID_10G_BASE_T 0x1586 #define I40E_DEV_ID_20G_KR2 0x1587 +#define I40E_DEV_ID_20G_KR2_A 0x1588 +#define I40E_DEV_ID_10G_BASE_T4 0x1589 #define I40E_DEV_ID_VF 0x154C #define I40E_DEV_ID_VF_HV 0x1571 #define I40E_DEV_ID_SFP_X722 0x37D0 @@ -158,14 +160,14 @@ enum i40e_set_fc_aq_failures { }; enum i40e_vsi_type { - I40E_VSI_MAIN = 0, - I40E_VSI_VMDQ1, - I40E_VSI_VMDQ2, - I40E_VSI_CTRL, - I40E_VSI_FCOE, - I40E_VSI_MIRROR, - I40E_VSI_SRIOV, - I40E_VSI_FDIR, + I40E_VSI_MAIN = 0, + I40E_VSI_VMDQ1 = 1, + I40E_VSI_VMDQ2 = 2, + I40E_VSI_CTRL = 3, + I40E_VSI_FCOE = 4, + I40E_VSI_MIRROR = 5, + I40E_VSI_SRIOV = 6, + I40E_VSI_FDIR = 7, I40E_VSI_TYPE_UNKNOWN }; @@ -305,12 +307,17 @@ enum i40e_nvmupd_cmd { I40E_NVMUPD_CSUM_CON, I40E_NVMUPD_CSUM_SA, I40E_NVMUPD_CSUM_LCB, + I40E_NVMUPD_STATUS, + I40E_NVMUPD_EXEC_AQ, + I40E_NVMUPD_GET_AQ_RESULT, }; enum i40e_nvmupd_state { I40E_NVMUPD_STATE_INIT, I40E_NVMUPD_STATE_READING, - I40E_NVMUPD_STATE_WRITING + I40E_NVMUPD_STATE_WRITING, + I40E_NVMUPD_STATE_INIT_WAIT, + I40E_NVMUPD_STATE_WRITE_WAIT, }; /* nvm_access definition and its masks/shifts need to be accessible to @@ -329,6 +336,7 @@ enum i40e_nvmupd_state { #define I40E_NVM_SA (I40E_NVM_SNT | I40E_NVM_LCB) #define I40E_NVM_ERA 0x4 #define I40E_NVM_CSUM 0x8 +#define I40E_NVM_EXEC 0xf #define I40E_NVM_ADAPT_SHIFT 16 #define I40E_NVM_ADAPT_MASK (0xffff << I40E_NVM_ADAPT_SHIFT) @@ -492,6 +500,8 @@ struct i40e_hw { /* state of nvm update process */ enum i40e_nvmupd_state nvmupd_state; + struct i40e_aq_desc nvm_wb_desc; + struct i40e_virt_mem nvm_buff; /* HMC info */ struct i40e_hmc_info hmc; /* HMC info struct */ @@ -500,8 +510,9 @@ struct i40e_hw { u16 dcbx_status; /* DCBX info */ - struct i40e_dcbx_config local_dcbx_config; - struct i40e_dcbx_config remote_dcbx_config; + struct i40e_dcbx_config local_dcbx_config; /* Oper/Local Cfg */ + struct i40e_dcbx_config remote_dcbx_config; /* Peer Cfg */ + struct i40e_dcbx_config desired_dcbx_config; /* CEE Desired Cfg */ /* debug mask */ u32 debug_mask; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h index 0f8d4156f8b1..95d0f8c5d484 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h @@ -81,7 +81,6 @@ enum i40e_virtchnl_ops { I40E_VIRTCHNL_OP_GET_STATS = 15, I40E_VIRTCHNL_OP_FCOE = 16, I40E_VIRTCHNL_OP_EVENT = 17, - I40E_VIRTCHNL_OP_CONFIG_RSS = 18, }; /* Virtual channel message descriptor. This overlays the admin queue diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index d99c116032f3..ee747dc5d617 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -536,6 +536,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_idx = vsi->idx; vf->lan_vsi_id = vsi->id; /* If the port VLAN has been configured and then the @@ -561,7 +562,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) } /* program mac filter */ - ret = i40e_sync_vsi_filters(vsi); + ret = i40e_sync_vsi_filters(vsi, false); if (ret) dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); @@ -605,6 +606,7 @@ static void i40e_enable_vf_mappings(struct i40e_vf *vf) /* map PF queues to VF queues */ 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++; @@ -872,6 +874,11 @@ void i40e_free_vfs(struct i40e_pf *pf) i40e_vsi_control_rings(pf->vsi[pf->vf[i].lan_vsi_idx], false); + for (i = 0; i < pf->num_alloc_vfs; i++) + if (test_bit(I40E_VF_STAT_INIT, &pf->vf[i].vf_states)) + i40e_vsi_control_rings(pf->vsi[pf->vf[i].lan_vsi_idx], + false); + /* Disable IOV before freeing resources. This lets any VF drivers * running in the host get themselves cleaned up before we yank * the carpet out from underneath their feet. @@ -986,24 +993,26 @@ static int i40e_pci_sriov_enable(struct pci_dev *pdev, int num_vfs) int pre_existing_vfs = pci_num_vf(pdev); int err = 0; - if (pf->state & __I40E_TESTING) { + if (test_bit(__I40E_TESTING, &pf->state)) { dev_warn(&pdev->dev, "Cannot enable SR-IOV virtual functions while the device is undergoing diagnostic testing\n"); err = -EPERM; goto err_out; } - dev_info(&pdev->dev, "Allocating %d VFs.\n", num_vfs); if (pre_existing_vfs && pre_existing_vfs != num_vfs) i40e_free_vfs(pf); else if (pre_existing_vfs && pre_existing_vfs == num_vfs) goto out; if (num_vfs > pf->num_req_vfs) { + dev_warn(&pdev->dev, "Unable to enable %d VFs. Limited to %d VFs due to device resource constraints.\n", + num_vfs, pf->num_req_vfs); err = -EPERM; goto err_out; } + dev_info(&pdev->dev, "Allocating %d VFs.\n", num_vfs); err = i40e_alloc_vfs(pf, num_vfs); if (err) { dev_warn(&pdev->dev, "Failed to enable SR-IOV: %d\n", err); @@ -1201,10 +1210,12 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) 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_idx]->alloc_queue_pairs; - memcpy(vfres->vsi_res[i].default_mac_addr, - vf->default_lan_addr.addr, ETH_ALEN); + vfres->vsi_res[i].num_queue_pairs = vsi->alloc_queue_pairs; + /* VFs only use TC 0 */ + vfres->vsi_res[i].qset_handle + = le16_to_cpu(vsi->info.qs_handle[0]); + ether_addr_copy(vfres->vsi_res[i].default_mac_addr, + vf->default_lan_addr.addr); i++; } set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); @@ -1605,7 +1616,7 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) } /* program the updated filter list */ - if (i40e_sync_vsi_filters(vsi)) + if (i40e_sync_vsi_filters(vsi, false)) dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n"); error_param: @@ -1656,7 +1667,7 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) I40E_VLAN_ANY, true, false); /* program the updated filter list */ - if (i40e_sync_vsi_filters(vsi)) + if (i40e_sync_vsi_filters(vsi, false)) dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n"); error_param: @@ -1708,6 +1719,7 @@ static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) for (i = 0; i < vfl->num_elements; i++) { /* add new VLAN filter */ int ret = i40e_vsi_add_vlan(vsi, vfl->vlan_id[i]); + if (ret) dev_err(&pf->pdev->dev, "Unable to add VF vlan filter %d, error %d\n", @@ -1759,6 +1771,7 @@ static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) for (i = 0; i < vfl->num_elements; i++) { int ret = i40e_vsi_kill_vlan(vsi, vfl->vlan_id[i]); + if (ret) dev_err(&pf->pdev->dev, "Unable to delete VF vlan filter %d, error %d\n", @@ -1870,7 +1883,6 @@ static int i40e_vc_validate_vf_msg(struct i40e_vf *vf, u32 v_opcode, case I40E_VIRTCHNL_OP_UNKNOWN: default: return -EPERM; - break; } /* few more checks */ if ((valid_len != msglen) || (err_msg_format)) { @@ -2062,7 +2074,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", mac, vf_id); /* program mac filter */ - if (i40e_sync_vsi_filters(vsi)) { + if (i40e_sync_vsi_filters(vsi, false)) { dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); ret = -EIO; goto error_param; @@ -2089,6 +2101,7 @@ error_param: int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos) { + u16 vlanprio = vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT); struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; struct i40e_vsi *vsi; @@ -2116,8 +2129,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, goto error_pvid; } - if (le16_to_cpu(vsi->info.pvid) == - (vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT))) + if (le16_to_cpu(vsi->info.pvid) == vlanprio) /* duplicate request, so just return success */ goto error_pvid; @@ -2141,7 +2153,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, * MAC addresses deleted. */ if ((!(vlan_id || qos) || - (vlan_id | qos) != le16_to_cpu(vsi->info.pvid)) && + vlanprio != le16_to_cpu(vsi->info.pvid)) && vsi->info.pvid) ret = i40e_vsi_add_vlan(vsi, I40E_VLAN_ANY); @@ -2156,8 +2168,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, } } if (vlan_id || qos) - ret = i40e_vsi_add_pvid(vsi, - vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT)); + ret = i40e_vsi_add_pvid(vsi, vlanprio); else i40e_vsi_remove_pvid(vsi); @@ -2310,7 +2321,7 @@ int i40e_ndo_get_vf_config(struct net_device *netdev, ivi->vf = vf_id; - memcpy(&ivi->mac, vf->default_lan_addr.addr, ETH_ALEN); + ether_addr_copy(ivi->mac, vf->default_lan_addr.addr); ivi->max_tx_rate = vf->tx_rate; ivi->min_tx_rate = 0; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c index f08450b90774..3eba36913c1d 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c @@ -469,8 +469,12 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.asq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_asq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.asq.head, 0); @@ -479,16 +483,13 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) wr32(hw, hw->aq.asq.bal, 0); wr32(hw, hw->aq.asq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.asq_mutex); - hw->aq.asq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_asq_bufs(hw); +shutdown_asq_out: mutex_unlock(&hw->aq.asq_mutex); - return ret_code; } @@ -502,8 +503,12 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.arq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.arq_mutex); + + if (hw->aq.arq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_arq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.arq.head, 0); @@ -512,16 +517,13 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) wr32(hw, hw->aq.arq.bal, 0); wr32(hw, hw->aq.arq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.arq_mutex); - hw->aq.arq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_arq_bufs(hw); +shutdown_arq_out: mutex_unlock(&hw->aq.arq_mutex); - return ret_code; } @@ -596,6 +598,9 @@ i40e_status i40evf_shutdown_adminq(struct i40e_hw *hw) /* destroy the locks */ + if (hw->nvm_buff.va) + i40e_free_virt_mem(hw, &hw->nvm_buff); + return ret_code; } @@ -617,8 +622,7 @@ static u16 i40e_clean_asq(struct i40e_hw *hw) details = I40E_ADMINQ_DETAILS(*asq, ntc); while (rd32(hw, hw->aq.asq.head) != ntc) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "%s: ntc %d head %d.\n", __func__, ntc, - rd32(hw, hw->aq.asq.head)); + "ntc %d head %d.\n", ntc, rd32(hw, hw->aq.asq.head)); if (details->callback) { I40E_ADMINQ_CALLBACK cb_func = @@ -682,19 +686,23 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, u16 retval = 0; u32 val = 0; - val = rd32(hw, hw->aq.asq.head); - if (val >= hw->aq.num_asq_entries) { + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: head overrun at %d\n", val); + "AQTX: Admin queue not initialized.\n"); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } - if (hw->aq.asq.count == 0) { + hw->aq.asq_last_status = I40E_AQ_RC_OK; + + val = rd32(hw, hw->aq.asq.head); + if (val >= hw->aq.num_asq_entries) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: Admin queue not initialized.\n"); + "AQTX: head overrun at %d\n", val); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use); @@ -719,8 +727,6 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, desc->flags &= ~cpu_to_le16(details->flags_dis); desc->flags |= cpu_to_le16(details->flags_ena); - mutex_lock(&hw->aq.asq_mutex); - if (buff_size > hw->aq.asq_buf_size) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, @@ -830,6 +836,10 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, i40evf_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, buff, buff_size); + /* save writeback aq if requested */ + if (details->wb_desc) + *details->wb_desc = *desc_on_ring; + /* update the error if time out occurred */ if ((!cmd_completed) && (!details->async && !details->postpone)) { @@ -841,7 +851,6 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, asq_send_command_error: mutex_unlock(&hw->aq.asq_mutex); -asq_send_command_exit: return status; } @@ -887,6 +896,13 @@ i40e_status i40evf_clean_arq_element(struct i40e_hw *hw, /* take the lock before we start messing with the ring */ mutex_lock(&hw->aq.arq_mutex); + if (hw->aq.arq.count == 0) { + i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, + "AQRX: Admin queue not initialized.\n"); + ret_code = I40E_ERR_QUEUE_EMPTY; + goto clean_arq_element_err; + } + /* set next_to_use to head */ ntu = (rd32(hw, hw->aq.arq.head) & I40E_VF_ARQH1_ARQH_MASK); if (ntu == ntc) { @@ -948,6 +964,8 @@ clean_arq_element_out: /* Set pending if needed, unlock and return */ if (pending != NULL) *pending = (ntc > ntu ? hw->aq.arq.count : 0) + (ntu - ntc); + +clean_arq_element_err: mutex_unlock(&hw->aq.arq_mutex); return ret_code; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h index ef43d68f67b3..a3eae5d9a2bd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h @@ -69,6 +69,7 @@ struct i40e_asq_cmd_details { u16 flags_dis; bool async; bool postpone; + struct i40e_aq_desc *wb_desc; }; #define I40E_ADMINQ_DETAILS(R, i) \ @@ -108,9 +109,10 @@ struct i40e_adminq_info { /** * i40e_aq_rc_to_posix - convert errors to user-land codes - * aq_rc: AdminQ error code to convert + * aq_ret: AdminQ handler error code can override aq_rc + * aq_rc: AdminQ firmware error code to convert **/ -static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc) +static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) { int aq_to_posix[] = { 0, /* I40E_AQ_RC_OK */ @@ -142,8 +144,9 @@ static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc) if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT) return -EAGAIN; - if (aq_rc >= ARRAY_SIZE(aq_to_posix)) + if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0])))) return -ERANGE; + return aq_to_posix[aq_rc]; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index d45d0ae6bd3b..b98b642b897a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -51,7 +51,9 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw) case I40E_DEV_ID_QSFP_B: case I40E_DEV_ID_QSFP_C: case I40E_DEV_ID_10G_BASE_T: + case I40E_DEV_ID_10G_BASE_T4: case I40E_DEV_ID_20G_KR2: + case I40E_DEV_ID_20G_KR2_A: hw->mac.type = I40E_MAC_XL710; break; case I40E_DEV_ID_SFP_X722: @@ -990,10 +992,10 @@ void i40e_vf_parse_hw_config(struct i40e_hw *hw, I40E_VIRTCHNL_VF_OFFLOAD_FCOE) ? 1 : 0; for (i = 0; i < msg->num_vsis; i++) { if (vsi_res->vsi_type == I40E_VSI_SRIOV) { - memcpy(hw->mac.perm_addr, vsi_res->default_mac_addr, - ETH_ALEN); - memcpy(hw->mac.addr, vsi_res->default_mac_addr, - ETH_ALEN); + ether_addr_copy(hw->mac.perm_addr, + vsi_res->default_mac_addr); + ether_addr_copy(hw->mac.addr, + vsi_res->default_mac_addr); } vsi_res++; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 7e91d825c760..0e71eb4633d5 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -140,65 +140,6 @@ static inline u32 i40e_get_head(struct i40e_ring *tx_ring) return le32_to_cpu(*(volatile __le32 *)head); } -/** - * i40e_get_tx_pending - how many tx descriptors not processed - * @tx_ring: the ring of descriptors - * - * Since there is no access to the ring head register - * in XL710, we need to use our local copies - **/ -static u32 i40e_get_tx_pending(struct i40e_ring *ring) -{ - u32 head, tail; - - head = i40e_get_head(ring); - tail = readl(ring->tail); - - if (head != tail) - return (head < tail) ? - tail - head : (tail + ring->count - head); - - return 0; -} - -/** - * i40e_check_tx_hang - Is there a hang in the Tx queue - * @tx_ring: the ring of descriptors - **/ -static bool i40e_check_tx_hang(struct i40e_ring *tx_ring) -{ - u32 tx_done = tx_ring->stats.packets; - u32 tx_done_old = tx_ring->tx_stats.tx_done_old; - u32 tx_pending = i40e_get_tx_pending(tx_ring); - bool ret = false; - - clear_check_for_tx_hang(tx_ring); - - /* Check for a hung queue, but be thorough. This verifies - * that a transmit has been completed since the previous - * check AND there is at least one packet pending. The - * ARMED bit is set to indicate a potential hang. The - * bit is cleared if a pause frame is received to remove - * false hang detection due to PFC or 802.3x frames. By - * requiring this to fail twice we avoid races with - * PFC clearing the ARMED bit and conditions where we - * run the check_tx_hang logic with a transmit completion - * pending but without time to complete it yet. - */ - if ((tx_done_old == tx_done) && tx_pending) { - /* make sure it is true for two checks in a row */ - ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED, - &tx_ring->state); - } else if (tx_done_old == tx_done && - (tx_pending < I40E_MIN_DESC_PENDING) && (tx_pending > 0)) { - /* update completed stats and disarm the hang check */ - tx_ring->tx_stats.tx_done_old = tx_done; - clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state); - } - - return ret; -} - #define WB_STRIDE 0x3 /** @@ -304,36 +245,15 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) tx_ring->q_vector->tx.total_bytes += total_bytes; tx_ring->q_vector->tx.total_packets += total_packets; + /* check to see if there are any non-cache aligned descriptors + * waiting to be written back, and kick the hardware to force + * them to be written back in case of napi polling + */ if (budget && !((i & WB_STRIDE) == WB_STRIDE) && !test_bit(__I40E_DOWN, &tx_ring->vsi->state) && (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) tx_ring->arm_wb = true; - else - tx_ring->arm_wb = false; - - if (check_for_tx_hang(tx_ring) && i40e_check_tx_hang(tx_ring)) { - /* schedule immediate reset if we believe we hung */ - dev_info(tx_ring->dev, "Detected Tx Unit Hang\n" - " VSI <%d>\n" - " Tx Queue <%d>\n" - " next_to_use <%x>\n" - " next_to_clean <%x>\n", - tx_ring->vsi->seid, - tx_ring->queue_index, - tx_ring->next_to_use, i); - - netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index); - - dev_info(tx_ring->dev, - "tx hang detected on queue %d, resetting adapter\n", - tx_ring->queue_index); - - tx_ring->netdev->netdev_ops->ndo_tx_timeout(tx_ring->netdev); - - /* the adapter is about to reset, no point in enabling stuff */ - return true; - } netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev, tx_ring->queue_index), @@ -355,16 +275,16 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) } } - return budget > 0; + return !!budget; } /** - * i40e_force_wb -Arm hardware to do a wb on noncache aligned descriptors + * i40evf_force_wb -Arm hardware to do a wb on noncache aligned descriptors * @vsi: the VSI we care about * @q_vector: the vector on which to force writeback * **/ -static void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) +static void i40evf_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) { u16 flags = q_vector->tx.ring[0].flags; @@ -997,7 +917,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) unsigned int total_rx_bytes = 0, total_rx_packets = 0; u16 rx_packet_len, rx_header_len, rx_sph, rx_hbo; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); - const int current_node = numa_node_id(); + const int current_node = numa_mem_id(); struct i40e_vsi *vsi = rx_ring->vsi; u16 i = rx_ring->next_to_clean; union i40e_rx_desc *rx_desc; @@ -1067,6 +987,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) cleaned_count++; if (rx_hbo || rx_sph) { int len; + if (rx_hbo) len = I40E_RX_HDR_SIZE; else @@ -1240,9 +1161,6 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) /* ERR_MASK will only have valid bits if EOP set */ if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) { dev_kfree_skb_any(skb); - /* TODO: shouldn't we increment a counter indicating the - * drop? - */ continue; } @@ -1366,6 +1284,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) i40e_for_each_ring(ring, q_vector->tx) { clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit); arm_wb |= ring->arm_wb; + ring->arm_wb = false; } /* We attempt to distribute budget to each Rx queue fairly, but don't @@ -1385,7 +1304,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) /* If work not completed, return budget and polling will return */ if (!clean_complete) { if (arm_wb) - i40e_force_wb(vsi, q_vector); + i40evf_force_wb(vsi, q_vector); return budget; } @@ -1437,6 +1356,7 @@ static inline int i40evf_tx_prepare_vlan_flags(struct sk_buff *skb, /* else if it is a SW VLAN, check the next protocol and store the tag */ } else if (protocol == htons(ETH_P_8021Q)) { struct vlan_hdr *vhdr, _vhdr; + vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), &_vhdr); if (!vhdr) return -EINVAL; @@ -1979,6 +1899,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, u32 td_cmd = 0; u8 hdr_len = 0; int tso; + if (0 == i40evf_xmit_descriptor_count(skb, tx_ring)) return NETDEV_TX_BUSY; @@ -2006,10 +1927,11 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, else if (tso) tx_flags |= I40E_TX_FLAGS_TSO; - if (i40e_chk_linearize(skb, tx_flags)) + if (i40e_chk_linearize(skb, tx_flags)) { if (skb_linearize(skb)) goto out_drop; - + tx_ring->tx_stats.tx_linearize++; + } skb_tx_timestamp(skb); /* always enable CRC insertion offload */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 9a30f5d8c089..0c13ece00366 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -164,6 +164,7 @@ struct i40e_tx_buffer { }; unsigned int bytecount; unsigned short gso_segs; + DEFINE_DMA_UNMAP_ADDR(dma); DEFINE_DMA_UNMAP_LEN(len); u32 tx_flags; @@ -187,6 +188,7 @@ struct i40e_tx_queue_stats { u64 restart_queue; u64 tx_busy; u64 tx_done_old; + u64 tx_linearize; }; struct i40e_rx_queue_stats { @@ -198,8 +200,6 @@ struct i40e_rx_queue_stats { enum i40e_ring_state_t { __I40E_TX_FDIR_INIT_DONE, __I40E_TX_XPS_INIT_DONE, - __I40E_TX_DETECT_HANG, - __I40E_HANG_CHECK_ARMED, __I40E_RX_PS_ENABLED, __I40E_RX_16BYTE_DESC_ENABLED, }; @@ -210,12 +210,6 @@ enum i40e_ring_state_t { set_bit(__I40E_RX_PS_ENABLED, &(ring)->state) #define clear_ring_ps_enabled(ring) \ clear_bit(__I40E_RX_PS_ENABLED, &(ring)->state) -#define check_for_tx_hang(ring) \ - test_bit(__I40E_TX_DETECT_HANG, &(ring)->state) -#define set_check_for_tx_hang(ring) \ - set_bit(__I40E_TX_DETECT_HANG, &(ring)->state) -#define clear_check_for_tx_hang(ring) \ - clear_bit(__I40E_TX_DETECT_HANG, &(ring)->state) #define ring_is_16byte_desc_enabled(ring) \ test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state) #define set_ring_16byte_desc_enabled(ring) \ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index 24a2693869a1..a59b60ffd0ce 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -45,6 +45,8 @@ #define I40E_DEV_ID_QSFP_C 0x1585 #define I40E_DEV_ID_10G_BASE_T 0x1586 #define I40E_DEV_ID_20G_KR2 0x1587 +#define I40E_DEV_ID_20G_KR2_A 0x1588 +#define I40E_DEV_ID_10G_BASE_T4 0x1589 #define I40E_DEV_ID_VF 0x154C #define I40E_DEV_ID_VF_HV 0x1571 #define I40E_DEV_ID_SFP_X722 0x37D0 @@ -158,14 +160,14 @@ enum i40e_set_fc_aq_failures { }; enum i40e_vsi_type { - I40E_VSI_MAIN = 0, - I40E_VSI_VMDQ1, - I40E_VSI_VMDQ2, - I40E_VSI_CTRL, - I40E_VSI_FCOE, - I40E_VSI_MIRROR, - I40E_VSI_SRIOV, - I40E_VSI_FDIR, + I40E_VSI_MAIN = 0, + I40E_VSI_VMDQ1 = 1, + I40E_VSI_VMDQ2 = 2, + I40E_VSI_CTRL = 3, + I40E_VSI_FCOE = 4, + I40E_VSI_MIRROR = 5, + I40E_VSI_SRIOV = 6, + I40E_VSI_FDIR = 7, I40E_VSI_TYPE_UNKNOWN }; @@ -304,12 +306,17 @@ enum i40e_nvmupd_cmd { I40E_NVMUPD_CSUM_CON, I40E_NVMUPD_CSUM_SA, I40E_NVMUPD_CSUM_LCB, + I40E_NVMUPD_STATUS, + I40E_NVMUPD_EXEC_AQ, + I40E_NVMUPD_GET_AQ_RESULT, }; enum i40e_nvmupd_state { I40E_NVMUPD_STATE_INIT, I40E_NVMUPD_STATE_READING, - I40E_NVMUPD_STATE_WRITING + I40E_NVMUPD_STATE_WRITING, + I40E_NVMUPD_STATE_INIT_WAIT, + I40E_NVMUPD_STATE_WRITE_WAIT, }; /* nvm_access definition and its masks/shifts need to be accessible to @@ -328,6 +335,7 @@ enum i40e_nvmupd_state { #define I40E_NVM_SA (I40E_NVM_SNT | I40E_NVM_LCB) #define I40E_NVM_ERA 0x4 #define I40E_NVM_CSUM 0x8 +#define I40E_NVM_EXEC 0xf #define I40E_NVM_ADAPT_SHIFT 16 #define I40E_NVM_ADAPT_MASK (0xffff << I40E_NVM_ADAPT_SHIFT) @@ -486,6 +494,8 @@ struct i40e_hw { /* state of nvm update process */ enum i40e_nvmupd_state nvmupd_state; + struct i40e_aq_desc nvm_wb_desc; + struct i40e_virt_mem nvm_buff; /* HMC info */ struct i40e_hmc_info hmc; /* HMC info struct */ @@ -494,8 +504,9 @@ struct i40e_hw { u16 dcbx_status; /* DCBX info */ - struct i40e_dcbx_config local_dcbx_config; - struct i40e_dcbx_config remote_dcbx_config; + struct i40e_dcbx_config local_dcbx_config; /* Oper/Local Cfg */ + struct i40e_dcbx_config remote_dcbx_config; /* Peer Cfg */ + struct i40e_dcbx_config desired_dcbx_config; /* CEE Desired Cfg */ /* debug mask */ u32 debug_mask; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h index e6db20e8a395..cadda642c98c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h @@ -81,7 +81,6 @@ enum i40e_virtchnl_ops { I40E_VIRTCHNL_OP_GET_STATS = 15, I40E_VIRTCHNL_OP_FCOE = 16, I40E_VIRTCHNL_OP_EVENT = 17, - I40E_VIRTCHNL_OP_CONFIG_RSS = 18, }; /* Virtual channel message descriptor. This overlays the admin queue diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 3817cbbf45e6..e7a223ea6c25 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -48,10 +48,6 @@ #define DEFAULT_DEBUG_LEVEL_SHIFT 3 #define PFX "i40evf: " -#define DPRINTK(nlevel, klevel, fmt, args...) \ - ((void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \ - printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->netdev->name, \ - __func__ , ## args))) /* dummy struct to make common code less painful */ struct i40e_vsi { @@ -70,6 +66,7 @@ struct i40e_vsi { */ u16 rx_itr_setting; u16 tx_itr_setting; + u16 qs_handle; }; /* How many Rx Buffers do we bundle into one write to the hardware ? */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index e85849b9ff98..c00e4959f026 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -34,7 +34,7 @@ char i40evf_driver_name[] = "i40evf"; static const char i40evf_driver_string[] = "Intel(R) XL710/X710 Virtual Function Network Driver"; -#define DRV_VERSION "1.3.5" +#define DRV_VERSION "1.3.13" const char i40evf_driver_version[] = DRV_VERSION; static const char i40evf_copyright[] = "Copyright (c) 2013 - 2015 Intel Corporation."; @@ -489,8 +489,7 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) q_vector); if (err) { dev_info(&adapter->pdev->dev, - "%s: request_irq failed, error: %d\n", - __func__, err); + "Request_irq failed, error: %d\n", err); goto free_queue_irqs; } /* assign the mask for this irq */ @@ -731,6 +730,8 @@ static int i40evf_vlan_rx_add_vid(struct net_device *netdev, { struct i40evf_adapter *adapter = netdev_priv(netdev); + if (!VLAN_ALLOWED(adapter)) + return -EIO; if (i40evf_add_vlan(adapter, vid) == NULL) return -ENOMEM; return 0; @@ -746,8 +747,11 @@ static int i40evf_vlan_rx_kill_vid(struct net_device *netdev, { struct i40evf_adapter *adapter = netdev_priv(netdev); - i40evf_del_vlan(adapter, vid); - return 0; + if (VLAN_ALLOWED(adapter)) { + i40evf_del_vlan(adapter, vid); + return 0; + } + return -EIO; } /** @@ -856,6 +860,7 @@ static void i40evf_set_rx_mode(struct net_device *netdev) struct i40evf_mac_filter *f, *ftmp; struct netdev_hw_addr *uca; struct netdev_hw_addr *mca; + struct netdev_hw_addr *ha; int count = 50; /* add addr if not already in the filter list */ @@ -877,29 +882,27 @@ static void i40evf_set_rx_mode(struct net_device *netdev) } /* remove filter if not in netdev list */ list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) { - bool found = false; - - if (is_multicast_ether_addr(f->macaddr)) { - netdev_for_each_mc_addr(mca, netdev) { - if (ether_addr_equal(mca->addr, f->macaddr)) { - found = true; - break; - } - } - } else { - netdev_for_each_uc_addr(uca, netdev) { - if (ether_addr_equal(uca->addr, f->macaddr)) { - found = true; - break; - } - } - if (ether_addr_equal(f->macaddr, adapter->hw.mac.addr)) - found = true; - } - if (!found) { - f->remove = true; - adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER; - } + netdev_for_each_mc_addr(mca, netdev) + if (ether_addr_equal(mca->addr, f->macaddr)) + goto bottom_of_search_loop; + + netdev_for_each_uc_addr(uca, netdev) + if (ether_addr_equal(uca->addr, f->macaddr)) + goto bottom_of_search_loop; + + for_each_dev_addr(netdev, ha) + if (ether_addr_equal(ha->addr, f->macaddr)) + goto bottom_of_search_loop; + + if (ether_addr_equal(f->macaddr, adapter->hw.mac.addr)) + goto bottom_of_search_loop; + + /* f->macaddr wasn't found in uc, mc, or ha list so delete it */ + f->remove = true; + adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER; + +bottom_of_search_loop: + continue; } clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); } @@ -1165,7 +1168,7 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter) for (vector = 0; vector < v_budget; vector++) adapter->msix_entries[vector].entry = vector; - i40evf_acquire_msix_vectors(adapter, v_budget); + err = i40evf_acquire_msix_vectors(adapter, v_budget); out: adapter->netdev->real_num_tx_queues = pairs; @@ -1421,16 +1424,16 @@ static void i40evf_watchdog_task(struct work_struct *work) struct i40evf_adapter, watchdog_task); struct i40e_hw *hw = &adapter->hw; - uint32_t rstat_val; + u32 reg_val; if (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section)) goto restart_watchdog; if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) { - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if ((rstat_val == I40E_VFR_VFACTIVE) || - (rstat_val == I40E_VFR_COMPLETED)) { + reg_val = rd32(hw, I40E_VFGEN_RSTAT) & + I40E_VFGEN_RSTAT_VFR_STATE_MASK; + if ((reg_val == I40E_VFR_VFACTIVE) || + (reg_val == I40E_VFR_COMPLETED)) { /* A chance for redemption! */ dev_err(&adapter->pdev->dev, "Hardware came out of reset. Attempting reinit.\n"); adapter->state = __I40EVF_STARTUP; @@ -1455,11 +1458,8 @@ static void i40evf_watchdog_task(struct work_struct *work) goto watchdog_done; /* check for reset */ - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING) && - (rstat_val != I40E_VFR_VFACTIVE) && - (rstat_val != I40E_VFR_COMPLETED)) { + reg_val = rd32(hw, I40E_VF_ARQLEN1) & I40E_VF_ARQLEN1_ARQENABLE_MASK; + if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING) && !reg_val) { adapter->state = __I40EVF_RESETTING; adapter->flags |= I40EVF_FLAG_RESET_PENDING; dev_err(&adapter->pdev->dev, "Hardware reset detected\n"); @@ -1574,7 +1574,7 @@ static void i40evf_reset_task(struct work_struct *work) struct net_device *netdev = adapter->netdev; struct i40e_hw *hw = &adapter->hw; struct i40evf_mac_filter *f; - uint32_t rstat_val; + u32 reg_val; int i = 0, err; while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, @@ -1595,12 +1595,11 @@ static void i40evf_reset_task(struct work_struct *work) /* poll until we see the reset actually happen */ for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) { - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if ((rstat_val != I40E_VFR_VFACTIVE) && - (rstat_val != I40E_VFR_COMPLETED)) + reg_val = rd32(hw, I40E_VF_ARQLEN1) & + I40E_VF_ARQLEN1_ARQENABLE_MASK; + if (!reg_val) break; - usleep_range(500, 1000); + usleep_range(5000, 10000); } if (i == I40EVF_RESET_WAIT_COUNT) { dev_info(&adapter->pdev->dev, "Never saw reset\n"); @@ -1609,9 +1608,9 @@ static void i40evf_reset_task(struct work_struct *work) /* wait until the reset is complete and the PF is responding to us */ for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) { - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if (rstat_val == I40E_VFR_VFACTIVE) + reg_val = rd32(hw, I40E_VFGEN_RSTAT) & + I40E_VFGEN_RSTAT_VFR_STATE_MASK; + if (reg_val == I40E_VFR_VFACTIVE) break; msleep(I40EVF_RESET_WAIT_MS); } @@ -1623,7 +1622,7 @@ static void i40evf_reset_task(struct work_struct *work) /* reset never finished */ dev_err(&adapter->pdev->dev, "Reset never finished (%x)\n", - rstat_val); + reg_val); adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED; if (netif_running(adapter->netdev)) { @@ -1853,8 +1852,7 @@ static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter) if (!err) continue; dev_err(&adapter->pdev->dev, - "%s: Allocation for Tx Queue %u failed\n", - __func__, i); + "Allocation for Tx Queue %u failed\n", i); break; } @@ -1881,8 +1879,7 @@ static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter) if (!err) continue; dev_err(&adapter->pdev->dev, - "%s: Allocation for Rx Queue %u failed\n", - __func__, i); + "Allocation for Rx Queue %u failed\n", i); break; } return err; @@ -2118,6 +2115,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter) adapter->vsi.tx_itr_setting = (I40E_ITR_DYNAMIC | ITR_REG_TO_USEC(I40E_ITR_TX_DEF)); adapter->vsi.netdev = adapter->netdev; + adapter->vsi.qs_handle = adapter->vsi_res->qset_handle; return 0; } @@ -2300,8 +2298,7 @@ static void i40evf_init_task(struct work_struct *work) } return; restart: - schedule_delayed_work(&adapter->init_task, - msecs_to_jiffies(50)); + schedule_delayed_work(&adapter->init_task, msecs_to_jiffies(30)); return; err_register: @@ -2434,7 +2431,8 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) INIT_WORK(&adapter->adminq_task, i40evf_adminq_task); INIT_WORK(&adapter->watchdog_task, i40evf_watchdog_task); INIT_DELAYED_WORK(&adapter->init_task, i40evf_init_task); - schedule_delayed_work(&adapter->init_task, 10); + schedule_delayed_work(&adapter->init_task, + msecs_to_jiffies(5 * (pdev->devfn & 0x07))); return 0; @@ -2510,6 +2508,7 @@ static int i40evf_resume(struct pci_dev *pdev) rtnl_lock(); err = i40evf_set_interrupt_capability(adapter); if (err) { + rtnl_unlock(); dev_err(&pdev->dev, "Cannot enable MSI-X interrupts.\n"); return err; } diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index d4eb1a5e7d42..4f056efecba4 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -234,8 +234,8 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot configure queues, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES; @@ -288,8 +288,8 @@ void i40evf_enable_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot enable queues, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_ENABLE_QUEUES; @@ -313,8 +313,8 @@ void i40evf_disable_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot disable queues, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_DISABLE_QUEUES; @@ -341,8 +341,8 @@ void i40evf_map_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot map queues to vectors, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP; @@ -393,8 +393,8 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot add filters, command %d pending\n", + adapter->current_op); return; } list_for_each_entry(f, &adapter->mac_filter_list, list) { @@ -410,8 +410,7 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_ether_addr_list) + (count * sizeof(struct i40e_virtchnl_ether_addr)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many add MAC changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_ether_addr_list)) / sizeof(struct i40e_virtchnl_ether_addr); @@ -453,8 +452,8 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot remove filters, command %d pending\n", + adapter->current_op); return; } list_for_each_entry(f, &adapter->mac_filter_list, list) { @@ -470,8 +469,7 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_ether_addr_list) + (count * sizeof(struct i40e_virtchnl_ether_addr)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many delete MAC changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_ether_addr_list)) / sizeof(struct i40e_virtchnl_ether_addr); @@ -513,8 +511,8 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot add VLANs, command %d pending\n", + adapter->current_op); return; } @@ -531,8 +529,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_vlan_filter_list) + (count * sizeof(u16)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many add VLAN changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_vlan_filter_list)) / sizeof(u16); @@ -572,8 +569,8 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot remove VLANs, command %d pending\n", + adapter->current_op); return; } @@ -590,8 +587,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_vlan_filter_list) + (count * sizeof(u16)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many delete VLAN changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_vlan_filter_list)) / sizeof(u16); @@ -629,8 +625,8 @@ void i40evf_set_promiscuous(struct i40evf_adapter *adapter, int flags) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot set promiscuous mode, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE; @@ -720,17 +716,16 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, } break; default: - dev_err(&adapter->pdev->dev, - "%s: Unknown event %d from pf\n", - __func__, vpe->event); + dev_err(&adapter->pdev->dev, "Unknown event %d from PF\n", + vpe->event); break; } return; } if (v_retval) { - dev_err(&adapter->pdev->dev, "%s: PF returned error %d (%s) to our request %d\n", - __func__, v_retval, - i40evf_stat_str(&adapter->hw, v_retval), v_opcode); + dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n", + v_retval, i40evf_stat_str(&adapter->hw, v_retval), + v_opcode); } switch (v_opcode) { case I40E_VIRTCHNL_OP_GET_STATS: { @@ -756,6 +751,8 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, sizeof(struct i40e_virtchnl_vsi_resource); memcpy(adapter->vf_res, msg, min(msglen, len)); i40e_vf_parse_hw_config(&adapter->hw, adapter->vf_res); + /* restore current mac address */ + ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr); i40evf_process_config(adapter); } break; diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 212d668dabb3..1a2f1cc44b28 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -444,8 +444,8 @@ struct igb_adapter { struct ptp_pin_desc sdp_config[IGB_N_SDP]; struct { - struct timespec start; - struct timespec period; + struct timespec64 start; + struct timespec64 period; } perout[IGB_N_PEROUT]; char fw_version[32]; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index e174fbbdba40..7e6267503790 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -2986,6 +2986,9 @@ static int igb_sw_init(struct igb_adapter *adapter) } #endif /* CONFIG_PCI_IOV */ + /* Assume MSI-X interrupts, will be checked during IRQ allocation */ + adapter->flags |= IGB_FLAG_HAS_MSIX; + igb_probe_vfs(adapter); igb_init_queue_configuration(adapter); @@ -5389,7 +5392,7 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; struct ptp_clock_event event; - struct timespec ts; + struct timespec64 ts; u32 ack = 0, tsauxc, sec, nsec, tsicr = rd32(E1000_TSICR); if (tsicr & TSINTR_SYS_WRAP) { @@ -5409,10 +5412,11 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter) if (tsicr & TSINTR_TT0) { spin_lock(&adapter->tmreg_lock); - ts = timespec_add(adapter->perout[0].start, - adapter->perout[0].period); + ts = timespec64_add(adapter->perout[0].start, + adapter->perout[0].period); + /* u32 conversion of tv_sec is safe until y2106 */ wr32(E1000_TRGTTIML0, ts.tv_nsec); - wr32(E1000_TRGTTIMH0, ts.tv_sec); + wr32(E1000_TRGTTIMH0, (u32)ts.tv_sec); tsauxc = rd32(E1000_TSAUXC); tsauxc |= TSAUXC_EN_TT0; wr32(E1000_TSAUXC, tsauxc); @@ -5423,10 +5427,10 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter) if (tsicr & TSINTR_TT1) { spin_lock(&adapter->tmreg_lock); - ts = timespec_add(adapter->perout[1].start, - adapter->perout[1].period); + ts = timespec64_add(adapter->perout[1].start, + adapter->perout[1].period); wr32(E1000_TRGTTIML1, ts.tv_nsec); - wr32(E1000_TRGTTIMH1, ts.tv_sec); + wr32(E1000_TRGTTIMH1, (u32)ts.tv_sec); tsauxc = rd32(E1000_TSAUXC); tsauxc |= TSAUXC_EN_TT1; wr32(E1000_TSAUXC, tsauxc); diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 5982f28d521a..c44df87c38de 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -143,7 +143,7 @@ static void igb_ptp_write_i210(struct igb_adapter *adapter, * sub-nanosecond resolution. */ wr32(E1000_SYSTIML, ts->tv_nsec); - wr32(E1000_SYSTIMH, ts->tv_sec); + wr32(E1000_SYSTIMH, (u32)ts->tv_sec); } /** @@ -479,7 +479,7 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp, struct e1000_hw *hw = &igb->hw; u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh, freqout; unsigned long flags; - struct timespec ts; + struct timespec64 ts; int use_freq = 0, pin = -1; s64 ns; @@ -523,14 +523,14 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp, } ts.tv_sec = rq->perout.period.sec; ts.tv_nsec = rq->perout.period.nsec; - ns = timespec_to_ns(&ts); + ns = timespec64_to_ns(&ts); ns = ns >> 1; if (on && ns <= 70000000LL) { if (ns < 8LL) return -EINVAL; use_freq = 1; } - ts = ns_to_timespec(ns); + ts = ns_to_timespec64(ns); if (rq->perout.index == 1) { if (use_freq) { tsauxc_mask = TSAUXC_EN_CLK1 | TSAUXC_ST1; diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 686fa7184179..e86d41ed9260 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -2615,6 +2615,7 @@ static const struct net_device_ops igbvf_netdev_ops = { .ndo_poll_controller = igbvf_netpoll, #endif .ndo_set_features = igbvf_set_features, + .ndo_features_check = passthru_features_check, }; /** diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index a699c991ad2c..dda0f678339a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -594,6 +594,7 @@ struct ixgbe_mac_addr { /* default to trying for four seconds */ #define IXGBE_TRY_LINK_TIMEOUT (4 * HZ) +#define IXGBE_SFP_POLL_JIFFIES (2 * HZ) /* SFP poll every 2 seconds */ /* board specific private data structure */ struct ixgbe_adapter { @@ -707,6 +708,7 @@ struct ixgbe_adapter { u32 link_speed; bool link_up; + unsigned long sfp_poll_time; unsigned long link_check_timeout; struct timer_list service_timer; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index dd7062fed61a..a39afcf03e2c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -44,9 +44,8 @@ static void ixgbe_disable_tx_laser_multispeed_fiber(struct ixgbe_hw *hw); static void ixgbe_enable_tx_laser_multispeed_fiber(struct ixgbe_hw *hw); static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw); -static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete); +static void +ixgbe_set_hard_rate_select_speed(struct ixgbe_hw *, ixgbe_link_speed); static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete); @@ -109,6 +108,9 @@ static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw) if (hw->phy.multispeed_fiber) { /* Set up dual speed SFP+ support */ mac->ops.setup_link = &ixgbe_setup_mac_link_multispeed_fiber; + mac->ops.setup_mac_link = ixgbe_setup_mac_link_82599; + mac->ops.set_rate_select_speed = + ixgbe_set_hard_rate_select_speed; } else { if ((mac->ops.get_media_type(hw) == ixgbe_media_type_backplane) && @@ -646,176 +648,32 @@ static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw) } /** - * ixgbe_setup_mac_link_multispeed_fiber - Set MAC link speed - * @hw: pointer to hardware structure - * @speed: new link speed - * @autoneg_wait_to_complete: true when waiting for completion is needed + * ixgbe_set_hard_rate_select_speed - Set module link speed + * @hw: pointer to hardware structure + * @speed: link speed to set * - * Set the link speed in the AUTOC register and restarts link. - **/ -static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete) + * Set module link speed via RS0/RS1 rate select pins. + */ +static void +ixgbe_set_hard_rate_select_speed(struct ixgbe_hw *hw, ixgbe_link_speed speed) { - s32 status = 0; - ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN; - ixgbe_link_speed highest_link_speed = IXGBE_LINK_SPEED_UNKNOWN; - u32 speedcnt = 0; u32 esdp_reg = IXGBE_READ_REG(hw, IXGBE_ESDP); - u32 i = 0; - bool link_up = false; - bool autoneg = false; - - /* Mask off requested but non-supported speeds */ - status = hw->mac.ops.get_link_capabilities(hw, &link_speed, - &autoneg); - if (status != 0) - return status; - - speed &= link_speed; - - /* - * Try each speed one by one, highest priority first. We do this in - * software because 10gb fiber doesn't support speed autonegotiation. - */ - if (speed & IXGBE_LINK_SPEED_10GB_FULL) { - speedcnt++; - highest_link_speed = IXGBE_LINK_SPEED_10GB_FULL; - - /* If we already have link at this speed, just jump out */ - status = hw->mac.ops.check_link(hw, &link_speed, &link_up, - false); - if (status != 0) - return status; - - if ((link_speed == IXGBE_LINK_SPEED_10GB_FULL) && link_up) - goto out; - - /* Set the module link speed */ - switch (hw->phy.media_type) { - case ixgbe_media_type_fiber: - esdp_reg |= (IXGBE_ESDP_SDP5_DIR | IXGBE_ESDP_SDP5); - IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg); - IXGBE_WRITE_FLUSH(hw); - break; - case ixgbe_media_type_fiber_qsfp: - /* QSFP module automatically detects MAC link speed */ - break; - default: - hw_dbg(hw, "Unexpected media type.\n"); - break; - } - - /* Allow module to change analog characteristics (1G->10G) */ - msleep(40); - - status = ixgbe_setup_mac_link_82599(hw, - IXGBE_LINK_SPEED_10GB_FULL, - autoneg_wait_to_complete); - if (status != 0) - return status; - - /* Flap the tx laser if it has not already been done */ - if (hw->mac.ops.flap_tx_laser) - hw->mac.ops.flap_tx_laser(hw); - - /* - * Wait for the controller to acquire link. Per IEEE 802.3ap, - * Section 73.10.2, we may have to wait up to 500ms if KR is - * attempted. 82599 uses the same timing for 10g SFI. - */ - for (i = 0; i < 5; i++) { - /* Wait for the link partner to also set speed */ - msleep(100); - - /* If we have link, just jump out */ - status = hw->mac.ops.check_link(hw, &link_speed, - &link_up, false); - if (status != 0) - return status; - - if (link_up) - goto out; - } - } - - if (speed & IXGBE_LINK_SPEED_1GB_FULL) { - speedcnt++; - if (highest_link_speed == IXGBE_LINK_SPEED_UNKNOWN) - highest_link_speed = IXGBE_LINK_SPEED_1GB_FULL; - - /* If we already have link at this speed, just jump out */ - status = hw->mac.ops.check_link(hw, &link_speed, &link_up, - false); - if (status != 0) - return status; - - if ((link_speed == IXGBE_LINK_SPEED_1GB_FULL) && link_up) - goto out; - - /* Set the module link speed */ - switch (hw->phy.media_type) { - case ixgbe_media_type_fiber: - esdp_reg &= ~IXGBE_ESDP_SDP5; - esdp_reg |= IXGBE_ESDP_SDP5_DIR; - IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg); - IXGBE_WRITE_FLUSH(hw); - break; - case ixgbe_media_type_fiber_qsfp: - /* QSFP module automatically detects MAC link speed */ - break; - default: - hw_dbg(hw, "Unexpected media type.\n"); - break; - } - - /* Allow module to change analog characteristics (10G->1G) */ - msleep(40); - - status = ixgbe_setup_mac_link_82599(hw, - IXGBE_LINK_SPEED_1GB_FULL, - autoneg_wait_to_complete); - if (status != 0) - return status; - - /* Flap the tx laser if it has not already been done */ - if (hw->mac.ops.flap_tx_laser) - hw->mac.ops.flap_tx_laser(hw); - /* Wait for the link partner to also set speed */ - msleep(100); - - /* If we have link, just jump out */ - status = hw->mac.ops.check_link(hw, &link_speed, &link_up, - false); - if (status != 0) - return status; - - if (link_up) - goto out; + switch (speed) { + case IXGBE_LINK_SPEED_10GB_FULL: + esdp_reg |= (IXGBE_ESDP_SDP5_DIR | IXGBE_ESDP_SDP5); + break; + case IXGBE_LINK_SPEED_1GB_FULL: + esdp_reg &= ~IXGBE_ESDP_SDP5; + esdp_reg |= IXGBE_ESDP_SDP5_DIR; + break; + default: + hw_dbg(hw, "Invalid fixed module speed\n"); + return; } - /* - * We didn't get link. Configure back to the highest speed we tried, - * (if there was more than one). We call ourselves back with just the - * single highest speed that the user requested. - */ - if (speedcnt > 1) - status = ixgbe_setup_mac_link_multispeed_fiber(hw, - highest_link_speed, - autoneg_wait_to_complete); - -out: - /* Set autoneg_advertised value based on input link speed */ - hw->phy.autoneg_advertised = 0; - - if (speed & IXGBE_LINK_SPEED_10GB_FULL) - hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL; - - if (speed & IXGBE_LINK_SPEED_1GB_FULL) - hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL; - - return status; + IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg); + IXGBE_WRITE_FLUSH(hw); } /** @@ -1766,6 +1624,16 @@ s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw, IXGBE_WRITE_REG(hw, IXGBE_FDIRTCPM, ~fdirtcpm); IXGBE_WRITE_REG(hw, IXGBE_FDIRUDPM, ~fdirtcpm); + /* also use it for SCTP */ + switch (hw->mac.type) { + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + IXGBE_WRITE_REG(hw, IXGBE_FDIRSCTPM, ~fdirtcpm); + break; + default: + break; + } + /* store source and destination IP masks (big-enian) */ IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRSIP4M, ~input_mask->formatted.src_ip[0]); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 3f56a8080118..ce61b36b94f1 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -297,13 +297,13 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw) /* Setup flow control */ ret_val = ixgbe_setup_fc(hw); - if (!ret_val) - return 0; + if (ret_val) + return ret_val; /* Clear adapter stopped flag */ hw->adapter_stopped = false; - return ret_val; + return 0; } /** @@ -2164,10 +2164,11 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw) /* * In order to prevent Tx hangs when the internal Tx * switch is enabled we must set the high water mark - * to the maximum FCRTH value. This allows the Tx - * switch to function even under heavy Rx workloads. + * to the Rx packet buffer size - 24KB. This allows + * the Tx switch to function even under heavy Rx + * workloads. */ - fcrth = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 32; + fcrth = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 24576; } IXGBE_WRITE_REG(hw, IXGBE_FCRTH_82599(i), fcrth); @@ -2476,6 +2477,9 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw) hw_dbg(hw, "GIO Master Disable bit didn't clear - requesting resets\n"); hw->mac.flags |= IXGBE_FLAGS_DOUBLE_RESET_REQUIRED; + if (hw->mac.type >= ixgbe_mac_X550) + return 0; + /* * Before proceeding, make sure that the PCIe block does not have * transactions pending. @@ -3920,3 +3924,213 @@ bool ixgbe_mng_present(struct ixgbe_hw *hw) fwsm &= IXGBE_FWSM_MODE_MASK; return fwsm == IXGBE_FWSM_FW_MODE_PT; } + +/** + * ixgbe_setup_mac_link_multispeed_fiber - Set MAC link speed + * @hw: pointer to hardware structure + * @speed: new link speed + * @autoneg_wait_to_complete: true when waiting for completion is needed + * + * Set the link speed in the MAC and/or PHY register and restarts link. + */ +s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, + ixgbe_link_speed speed, + bool autoneg_wait_to_complete) +{ + ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN; + ixgbe_link_speed highest_link_speed = IXGBE_LINK_SPEED_UNKNOWN; + s32 status = 0; + u32 speedcnt = 0; + u32 i = 0; + bool autoneg, link_up = false; + + /* Mask off requested but non-supported speeds */ + status = hw->mac.ops.get_link_capabilities(hw, &link_speed, &autoneg); + if (status) + return status; + + speed &= link_speed; + + /* Try each speed one by one, highest priority first. We do this in + * software because 10Gb fiber doesn't support speed autonegotiation. + */ + if (speed & IXGBE_LINK_SPEED_10GB_FULL) { + speedcnt++; + highest_link_speed = IXGBE_LINK_SPEED_10GB_FULL; + + /* If we already have link at this speed, just jump out */ + status = hw->mac.ops.check_link(hw, &link_speed, &link_up, + false); + if (status) + return status; + + if (link_speed == IXGBE_LINK_SPEED_10GB_FULL && link_up) + goto out; + + /* Set the module link speed */ + switch (hw->phy.media_type) { + case ixgbe_media_type_fiber: + hw->mac.ops.set_rate_select_speed(hw, + IXGBE_LINK_SPEED_10GB_FULL); + break; + case ixgbe_media_type_fiber_qsfp: + /* QSFP module automatically detects MAC link speed */ + break; + default: + hw_dbg(hw, "Unexpected media type\n"); + break; + } + + /* Allow module to change analog characteristics (1G->10G) */ + msleep(40); + + status = hw->mac.ops.setup_mac_link(hw, + IXGBE_LINK_SPEED_10GB_FULL, + autoneg_wait_to_complete); + if (status) + return status; + + /* Flap the Tx laser if it has not already been done */ + if (hw->mac.ops.flap_tx_laser) + hw->mac.ops.flap_tx_laser(hw); + + /* Wait for the controller to acquire link. Per IEEE 802.3ap, + * Section 73.10.2, we may have to wait up to 500ms if KR is + * attempted. 82599 uses the same timing for 10g SFI. + */ + for (i = 0; i < 5; i++) { + /* Wait for the link partner to also set speed */ + msleep(100); + + /* If we have link, just jump out */ + status = hw->mac.ops.check_link(hw, &link_speed, + &link_up, false); + if (status) + return status; + + if (link_up) + goto out; + } + } + + if (speed & IXGBE_LINK_SPEED_1GB_FULL) { + speedcnt++; + if (highest_link_speed == IXGBE_LINK_SPEED_UNKNOWN) + highest_link_speed = IXGBE_LINK_SPEED_1GB_FULL; + + /* If we already have link at this speed, just jump out */ + status = hw->mac.ops.check_link(hw, &link_speed, &link_up, + false); + if (status) + return status; + + if (link_speed == IXGBE_LINK_SPEED_1GB_FULL && link_up) + goto out; + + /* Set the module link speed */ + switch (hw->phy.media_type) { + case ixgbe_media_type_fiber: + hw->mac.ops.set_rate_select_speed(hw, + IXGBE_LINK_SPEED_1GB_FULL); + break; + case ixgbe_media_type_fiber_qsfp: + /* QSFP module automatically detects link speed */ + break; + default: + hw_dbg(hw, "Unexpected media type\n"); + break; + } + + /* Allow module to change analog characteristics (10G->1G) */ + msleep(40); + + status = hw->mac.ops.setup_mac_link(hw, + IXGBE_LINK_SPEED_1GB_FULL, + autoneg_wait_to_complete); + if (status) + return status; + + /* Flap the Tx laser if it has not already been done */ + if (hw->mac.ops.flap_tx_laser) + hw->mac.ops.flap_tx_laser(hw); + + /* Wait for the link partner to also set speed */ + msleep(100); + + /* If we have link, just jump out */ + status = hw->mac.ops.check_link(hw, &link_speed, &link_up, + false); + if (status) + return status; + + if (link_up) + goto out; + } + + /* We didn't get link. Configure back to the highest speed we tried, + * (if there was more than one). We call ourselves back with just the + * single highest speed that the user requested. + */ + if (speedcnt > 1) + status = ixgbe_setup_mac_link_multispeed_fiber(hw, + highest_link_speed, + autoneg_wait_to_complete); + +out: + /* Set autoneg_advertised value based on input link speed */ + hw->phy.autoneg_advertised = 0; + + if (speed & IXGBE_LINK_SPEED_10GB_FULL) + hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL; + + if (speed & IXGBE_LINK_SPEED_1GB_FULL) + hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL; + + return status; +} + +/** + * ixgbe_set_soft_rate_select_speed - Set module link speed + * @hw: pointer to hardware structure + * @speed: link speed to set + * + * Set module link speed via the soft rate select. + */ +void ixgbe_set_soft_rate_select_speed(struct ixgbe_hw *hw, + ixgbe_link_speed speed) +{ + s32 status; + u8 rs, eeprom_data; + + switch (speed) { + case IXGBE_LINK_SPEED_10GB_FULL: + /* one bit mask same as setting on */ + rs = IXGBE_SFF_SOFT_RS_SELECT_10G; + break; + case IXGBE_LINK_SPEED_1GB_FULL: + rs = IXGBE_SFF_SOFT_RS_SELECT_1G; + break; + default: + hw_dbg(hw, "Invalid fixed module speed\n"); + return; + } + + /* Set RS0 */ + status = hw->phy.ops.read_i2c_byte(hw, IXGBE_SFF_SFF_8472_OSCB, + IXGBE_I2C_EEPROM_DEV_ADDR2, + &eeprom_data); + if (status) { + hw_dbg(hw, "Failed to read Rx Rate Select RS0\n"); + return; + } + + eeprom_data = (eeprom_data & ~IXGBE_SFF_SOFT_RS_SELECT_MASK) | rs; + + status = hw->phy.ops.write_i2c_byte(hw, IXGBE_SFF_SFF_8472_OSCB, + IXGBE_I2C_EEPROM_DEV_ADDR2, + eeprom_data); + if (status) { + hw_dbg(hw, "Failed to write Rx Rate Select RS0\n"); + return; + } +} diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h index 2f779f35dc4f..a0044e4a8b90 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h @@ -135,6 +135,11 @@ s32 ixgbe_get_thermal_sensor_data_generic(struct ixgbe_hw *hw); s32 ixgbe_init_thermal_sensor_thresh_generic(struct ixgbe_hw *hw); void ixgbe_disable_rx_generic(struct ixgbe_hw *hw); void ixgbe_enable_rx_generic(struct ixgbe_hw *hw); +s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, + ixgbe_link_speed speed, + bool autoneg_wait_to_complete); +void ixgbe_set_soft_rate_select_speed(struct ixgbe_hw *hw, + ixgbe_link_speed speed); #define IXGBE_FAILED_READ_REG 0xffffffffU #define IXGBE_FAILED_READ_CFG_DWORD 0xffffffffU diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c index 3b932fe64ab6..23277ab153b6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c @@ -259,7 +259,13 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc) fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE; IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), fcrtl); } else { - reg = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 32; + /* In order to prevent Tx hangs when the internal Tx + * switch is enabled we must set the high water mark + * to the Rx packet buffer size - 24KB. This allows + * the Tx switch to function even under heavy Rx + * workloads. + */ + reg = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 24576; IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), 0); } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index acb1b91408ec..191003901adb 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -79,7 +79,7 @@ char ixgbe_default_device_descr[] = static char ixgbe_default_device_descr[] = "Intel(R) 10 Gigabit Network Connection"; #endif -#define DRV_VERSION "4.0.1-k" +#define DRV_VERSION "4.2.1-k" const char ixgbe_driver_version[] = DRV_VERSION; static const char ixgbe_copyright[] = "Copyright (c) 1999-2015 Intel Corporation."; @@ -137,6 +137,7 @@ static const struct pci_device_id ixgbe_pci_tbl[] = { {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KX4), board_X550EM_x}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KR), board_X550EM_x}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_10G_T), board_X550EM_x}, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_SFP), board_X550EM_x}, /* required last entry */ {0, } }; @@ -1244,9 +1245,12 @@ static void ixgbe_update_tx_dca(struct ixgbe_adapter *adapter, int cpu) { struct ixgbe_hw *hw = &adapter->hw; - u32 txctrl = dca3_get_tag(tx_ring->dev, cpu); + u32 txctrl = 0; u16 reg_offset; + if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) + txctrl = dca3_get_tag(tx_ring->dev, cpu); + switch (hw->mac.type) { case ixgbe_mac_82598EB: reg_offset = IXGBE_DCA_TXCTRL(tx_ring->reg_idx); @@ -1278,9 +1282,11 @@ static void ixgbe_update_rx_dca(struct ixgbe_adapter *adapter, int cpu) { struct ixgbe_hw *hw = &adapter->hw; - u32 rxctrl = dca3_get_tag(rx_ring->dev, cpu); + u32 rxctrl = 0; u8 reg_idx = rx_ring->reg_idx; + if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) + rxctrl = dca3_get_tag(rx_ring->dev, cpu); switch (hw->mac.type) { case ixgbe_mac_82599EB: @@ -1297,6 +1303,7 @@ static void ixgbe_update_rx_dca(struct ixgbe_adapter *adapter, * which will cause the DCA tag to be cleared. */ rxctrl |= IXGBE_DCA_RXCTRL_DESC_RRO_EN | + IXGBE_DCA_RXCTRL_DATA_DCA_EN | IXGBE_DCA_RXCTRL_DESC_DCA_EN; IXGBE_WRITE_REG(hw, IXGBE_DCA_RXCTRL(reg_idx), rxctrl); @@ -1326,11 +1333,13 @@ static void ixgbe_setup_dca(struct ixgbe_adapter *adapter) { int i; - if (!(adapter->flags & IXGBE_FLAG_DCA_ENABLED)) - return; - /* always use CB2 mode, difference is masked in the CB driver */ - IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, 2); + if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) + IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, + IXGBE_DCA_CTRL_DCA_MODE_CB2); + else + IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, + IXGBE_DCA_CTRL_DCA_DISABLE); for (i = 0; i < adapter->num_q_vectors; i++) { adapter->q_vector[i]->cpu = -1; @@ -1353,7 +1362,8 @@ static int __ixgbe_notify_dca(struct device *dev, void *data) break; if (dca_add_requester(dev) == 0) { adapter->flags |= IXGBE_FLAG_DCA_ENABLED; - ixgbe_setup_dca(adapter); + IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, + IXGBE_DCA_CTRL_DCA_MODE_CB2); break; } /* Fall Through since DCA is disabled. */ @@ -1361,7 +1371,8 @@ static int __ixgbe_notify_dca(struct device *dev, void *data) if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) { dca_remove_requester(dev); adapter->flags &= ~IXGBE_FLAG_DCA_ENABLED; - IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, 1); + IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, + IXGBE_DCA_CTRL_DCA_DISABLE); } break; } @@ -2509,6 +2520,7 @@ static void ixgbe_check_sfp_event(struct ixgbe_adapter *adapter, u32 eicr) IXGBE_WRITE_REG(hw, IXGBE_EICR, eicr_mask); if (!test_bit(__IXGBE_DOWN, &adapter->state)) { adapter->flags2 |= IXGBE_FLAG2_SFP_NEEDS_RESET; + adapter->sfp_poll_time = 0; ixgbe_service_event_schedule(adapter); } } @@ -2631,6 +2643,8 @@ static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter, bool queues, case ixgbe_mac_X540: case ixgbe_mac_X550: case ixgbe_mac_X550EM_x: + if (adapter->hw.device_id == IXGBE_DEV_ID_X550EM_X_SFP) + mask |= IXGBE_EIMS_GPI_SDP0(&adapter->hw); if (adapter->hw.phy.type == ixgbe_phy_x550em_ext_t) mask |= IXGBE_EICR_GPI_SDP0_X540; mask |= IXGBE_EIMS_ECC; @@ -3786,8 +3800,6 @@ static void ixgbe_setup_rdrxctl(struct ixgbe_adapter *adapter) u32 rdrxctl = IXGBE_READ_REG(hw, IXGBE_RDRXCTL); switch (hw->mac.type) { - case ixgbe_mac_X550: - case ixgbe_mac_X550EM_x: case ixgbe_mac_82598EB: /* * For VMDq support of different descriptor types or @@ -3801,6 +3813,11 @@ static void ixgbe_setup_rdrxctl(struct ixgbe_adapter *adapter) */ rdrxctl |= IXGBE_RDRXCTL_MVMEN; break; + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + if (adapter->num_vfs) + rdrxctl |= IXGBE_RDRXCTL_PSP; + /* fall through for older HW */ case ixgbe_mac_82599EB: case ixgbe_mac_X540: /* Disable RSC for ACK packets */ @@ -4776,6 +4793,12 @@ static void ixgbe_configure(struct ixgbe_adapter *adapter) break; } +#ifdef CONFIG_IXGBE_DCA + /* configure DCA */ + if (adapter->flags & IXGBE_FLAG_DCA_CAPABLE) + ixgbe_setup_dca(adapter); +#endif /* CONFIG_IXGBE_DCA */ + #ifdef IXGBE_FCOE /* configure FCoE L2 filters, redirection table, and Rx control */ ixgbe_configure_fcoe(adapter); @@ -4802,6 +4825,7 @@ static void ixgbe_sfp_link_config(struct ixgbe_adapter *adapter) adapter->flags2 |= IXGBE_FLAG2_SEARCH_FOR_SFP; adapter->flags2 |= IXGBE_FLAG2_SFP_NEEDS_RESET; + adapter->sfp_poll_time = 0; } /** @@ -4892,9 +4916,6 @@ static void ixgbe_setup_gpie(struct ixgbe_adapter *adapter) case ixgbe_mac_82599EB: gpie |= IXGBE_SDP0_GPIEN_8259X; break; - case ixgbe_mac_X540: - gpie |= IXGBE_EIMS_TS; - break; default: break; } @@ -4904,9 +4925,15 @@ static void ixgbe_setup_gpie(struct ixgbe_adapter *adapter) if (adapter->flags & IXGBE_FLAG_FAN_FAIL_CAPABLE) gpie |= IXGBE_SDP1_GPIEN(hw); - if (hw->mac.type == ixgbe_mac_82599EB) { - gpie |= IXGBE_SDP1_GPIEN_8259X; - gpie |= IXGBE_SDP2_GPIEN_8259X; + switch (hw->mac.type) { + case ixgbe_mac_82599EB: + gpie |= IXGBE_SDP1_GPIEN_8259X | IXGBE_SDP2_GPIEN_8259X; + break; + case ixgbe_mac_X550EM_x: + gpie |= IXGBE_SDP0_GPIEN_X540; + break; + default: + break; } IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie); @@ -5229,11 +5256,6 @@ void ixgbe_down(struct ixgbe_adapter *adapter) ixgbe_clean_all_tx_rings(adapter); ixgbe_clean_all_rx_rings(adapter); - -#ifdef CONFIG_IXGBE_DCA - /* since we reset the hardware DCA settings were cleared */ - ixgbe_setup_dca(adapter); -#endif } /** @@ -6701,10 +6723,16 @@ static void ixgbe_sfp_detection_subtask(struct ixgbe_adapter *adapter) !(adapter->flags2 & IXGBE_FLAG2_SFP_NEEDS_RESET)) return; + if (adapter->sfp_poll_time && + time_after(adapter->sfp_poll_time, jiffies)) + return; /* If not yet time to poll for SFP */ + /* someone else is in init, wait until next service event */ if (test_and_set_bit(__IXGBE_IN_SFP_INIT, &adapter->state)) return; + adapter->sfp_poll_time = jiffies + IXGBE_SFP_POLL_JIFFIES - 1; + err = hw->phy.ops.identify_sfp(hw); if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) goto sfp_out; @@ -8704,8 +8732,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw->phy.reset_if_overtemp = true; err = hw->mac.ops.reset_hw(hw); hw->phy.reset_if_overtemp = false; - if (err == IXGBE_ERR_SFP_NOT_PRESENT && - hw->mac.type == ixgbe_mac_82598EB) { + if (err == IXGBE_ERR_SFP_NOT_PRESENT) { err = 0; } else if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) { e_dev_err("failed to load because an unsupported SFP+ or QSFP module type was detected.\n"); @@ -9017,7 +9044,8 @@ static void ixgbe_remove(struct pci_dev *pdev) if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) { adapter->flags &= ~IXGBE_FLAG_DCA_ENABLED; dca_remove_requester(&pdev->dev); - IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, 1); + IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, + IXGBE_DCA_CTRL_DCA_DISABLE); } #endif diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index 597d0b1c2370..fb8673d63806 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -100,16 +100,17 @@ static u8 ixgbe_ones_comp_byte_add(u8 add1, u8 add2) } /** - * ixgbe_read_i2c_combined_generic - Perform I2C read combined operation + * ixgbe_read_i2c_combined_generic_int - Perform I2C read combined operation * @hw: pointer to the hardware structure * @addr: I2C bus address to read from * @reg: I2C device register to read from * @val: pointer to location to receive read value + * @lock: true if to take and release semaphore * * Returns an error code on error. - **/ -s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, - u16 reg, u16 *val) + */ +static s32 ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 *val, bool lock) { u32 swfw_mask = hw->phy.phy_semaphore_mask; int max_retry = 10; @@ -124,7 +125,7 @@ s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, csum = ixgbe_ones_comp_byte_add(reg_high, reg & 0xFF); csum = ~csum; do { - if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) + if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) return IXGBE_ERR_SWFW_SYNC; ixgbe_i2c_start(hw); /* Device Address and write indication */ @@ -157,13 +158,15 @@ s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, if (ixgbe_clock_out_i2c_bit(hw, false)) goto fail; ixgbe_i2c_stop(hw); - hw->mac.ops.release_swfw_sync(hw, swfw_mask); + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); *val = (high_bits << 8) | low_bits; return 0; fail: ixgbe_i2c_bus_clear(hw); - hw->mac.ops.release_swfw_sync(hw, swfw_mask); + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); retry++; if (retry < max_retry) hw_dbg(hw, "I2C byte read combined error - Retry.\n"); @@ -175,17 +178,49 @@ fail: } /** - * ixgbe_write_i2c_combined_generic - Perform I2C write combined operation + * ixgbe_read_i2c_combined_generic - Perform I2C read combined operation + * @hw: pointer to the hardware structure + * @addr: I2C bus address to read from + * @reg: I2C device register to read from + * @val: pointer to location to receive read value + * + * Returns an error code on error. + */ +s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 *val) +{ + return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, true); +} + +/** + * ixgbe_read_i2c_combined_generic_unlocked - Unlocked I2C read combined + * @hw: pointer to the hardware structure + * @addr: I2C bus address to read from + * @reg: I2C device register to read from + * @val: pointer to location to receive read value + * + * Returns an error code on error. + */ +s32 ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 *val) +{ + return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, false); +} + +/** + * ixgbe_write_i2c_combined_generic_int - Perform I2C write combined operation * @hw: pointer to the hardware structure * @addr: I2C bus address to write to * @reg: I2C device register to write to * @val: value to write + * @lock: true if to take and release semaphore * * Returns an error code on error. - **/ -s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, - u8 addr, u16 reg, u16 val) + */ +static s32 ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 val, bool lock) { + u32 swfw_mask = hw->phy.phy_semaphore_mask; int max_retry = 1; int retry = 0; u8 reg_high; @@ -197,6 +232,8 @@ s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, csum = ixgbe_ones_comp_byte_add(csum, val & 0xFF); csum = ~csum; do { + if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) + return IXGBE_ERR_SWFW_SYNC; ixgbe_i2c_start(hw); /* Device Address and write indication */ if (ixgbe_out_i2c_byte_ack(hw, addr)) @@ -217,10 +254,14 @@ s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, if (ixgbe_out_i2c_byte_ack(hw, csum)) goto fail; ixgbe_i2c_stop(hw); + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); return 0; fail: ixgbe_i2c_bus_clear(hw); + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); retry++; if (retry < max_retry) hw_dbg(hw, "I2C byte write combined error - Retry.\n"); @@ -232,6 +273,36 @@ fail: } /** + * ixgbe_write_i2c_combined_generic - Perform I2C write combined operation + * @hw: pointer to the hardware structure + * @addr: I2C bus address to write to + * @reg: I2C device register to write to + * @val: value to write + * + * Returns an error code on error. + */ +s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, + u8 addr, u16 reg, u16 val) +{ + return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, true); +} + +/** + * ixgbe_write_i2c_combined_generic_unlocked - Unlocked I2C write combined + * @hw: pointer to the hardware structure + * @addr: I2C bus address to write to + * @reg: I2C device register to write to + * @val: value to write + * + * Returns an error code on error. + */ +s32 ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, + u8 addr, u16 reg, u16 val) +{ + return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, false); +} + +/** * ixgbe_identify_phy_generic - Get physical layer module * @hw: pointer to hardware structure * @@ -1100,6 +1171,9 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) return IXGBE_ERR_SFP_NOT_PRESENT; } + /* LAN ID is needed for sfp_type determination */ + hw->mac.ops.set_lan_id(hw); + status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_IDENTIFIER, &identifier); @@ -1107,9 +1181,6 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) if (status) goto err_read_i2c_eeprom; - /* LAN ID is needed for sfp_type determination */ - hw->mac.ops.set_lan_id(hw); - if (identifier != IXGBE_SFF_IDENTIFIER_SFP) { hw->phy.type = ixgbe_phy_sfp_unsupported; return IXGBE_ERR_SFP_NOT_SUPPORTED; @@ -1159,7 +1230,7 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) hw->phy.sfp_type = ixgbe_sfp_type_lr; else hw->phy.sfp_type = ixgbe_sfp_type_unknown; - } else if (hw->mac.type == ixgbe_mac_82599EB) { + } else { if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) { if (hw->bus.lan_id == 0) hw->phy.sfp_type = @@ -1660,26 +1731,46 @@ s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, } /** - * ixgbe_read_i2c_byte_generic - Reads 8 bit word over I2C + * ixgbe_is_sfp_probe - Returns true if SFP is being detected + * @hw: pointer to hardware structure + * @offset: eeprom offset to be read + * @addr: I2C address to be read + */ +static bool ixgbe_is_sfp_probe(struct ixgbe_hw *hw, u8 offset, u8 addr) +{ + if (addr == IXGBE_I2C_EEPROM_DEV_ADDR && + offset == IXGBE_SFF_IDENTIFIER && + hw->phy.sfp_type == ixgbe_sfp_type_not_present) + return true; + return false; +} + +/** + * ixgbe_read_i2c_byte_generic_int - Reads 8 bit word over I2C * @hw: pointer to hardware structure * @byte_offset: byte offset to read * @data: value read + * @lock: true if to take and release semaphore * * Performs byte read operation to SFP module's EEPROM over I2C interface at * a specified device address. - **/ -s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 dev_addr, u8 *data) + */ +static s32 ixgbe_read_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data, bool lock) { s32 status; u32 max_retry = 10; u32 retry = 0; u32 swfw_mask = hw->phy.phy_semaphore_mask; bool nack = true; + + if (ixgbe_is_sfp_probe(hw, byte_offset, dev_addr)) + max_retry = IXGBE_SFP_DETECT_RETRIES; + *data = 0; do { - if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) + if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) return IXGBE_ERR_SWFW_SYNC; ixgbe_i2c_start(hw); @@ -1721,12 +1812,16 @@ s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, goto fail; ixgbe_i2c_stop(hw); - break; + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + return 0; fail: ixgbe_i2c_bus_clear(hw); - hw->mac.ops.release_swfw_sync(hw, swfw_mask); - msleep(100); + if (lock) { + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + msleep(100); + } retry++; if (retry < max_retry) hw_dbg(hw, "I2C byte read error - Retrying.\n"); @@ -1735,29 +1830,60 @@ fail: } while (retry < max_retry); - hw->mac.ops.release_swfw_sync(hw, swfw_mask); - return status; } /** - * ixgbe_write_i2c_byte_generic - Writes 8 bit word over I2C + * ixgbe_read_i2c_byte_generic - Reads 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to read + * @data: value read + * + * Performs byte read operation to SFP module's EEPROM over I2C interface at + * a specified device address. + */ +s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data) +{ + return ixgbe_read_i2c_byte_generic_int(hw, byte_offset, dev_addr, + data, true); +} + +/** + * ixgbe_read_i2c_byte_generic_unlocked - Reads 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to read + * @data: value read + * + * Performs byte read operation to SFP module's EEPROM over I2C interface at + * a specified device address. + */ +s32 ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data) +{ + return ixgbe_read_i2c_byte_generic_int(hw, byte_offset, dev_addr, + data, false); +} + +/** + * ixgbe_write_i2c_byte_generic_int - Writes 8 bit word over I2C * @hw: pointer to hardware structure * @byte_offset: byte offset to write * @data: value to write + * @lock: true if to take and release semaphore * * Performs byte write operation to SFP module's EEPROM over I2C interface at * a specified device address. - **/ -s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 dev_addr, u8 data) + */ +static s32 ixgbe_write_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data, bool lock) { s32 status; u32 max_retry = 1; u32 retry = 0; u32 swfw_mask = hw->phy.phy_semaphore_mask; - if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) + if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) return IXGBE_ERR_SWFW_SYNC; do { @@ -1788,7 +1914,9 @@ s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, goto fail; ixgbe_i2c_stop(hw); - break; + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + return 0; fail: ixgbe_i2c_bus_clear(hw); @@ -1799,21 +1927,57 @@ fail: hw_dbg(hw, "I2C byte write error.\n"); } while (retry < max_retry); - hw->mac.ops.release_swfw_sync(hw, swfw_mask); + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); return status; } /** + * ixgbe_write_i2c_byte_generic - Writes 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @data: value to write + * + * Performs byte write operation to SFP module's EEPROM over I2C interface at + * a specified device address. + */ +s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data) +{ + return ixgbe_write_i2c_byte_generic_int(hw, byte_offset, dev_addr, + data, true); +} + +/** + * ixgbe_write_i2c_byte_generic_unlocked - Writes 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @data: value to write + * + * Performs byte write operation to SFP module's EEPROM over I2C interface at + * a specified device address. + */ +s32 ixgbe_write_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data) +{ + return ixgbe_write_i2c_byte_generic_int(hw, byte_offset, dev_addr, + data, false); +} + +/** * ixgbe_i2c_start - Sets I2C start condition * @hw: pointer to hardware structure * * Sets I2C start condition (High -> Low on SDA while SCL is High) + * Set bit-bang mode on X550 hardware. **/ static void ixgbe_i2c_start(struct ixgbe_hw *hw) { u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); + i2cctl |= IXGBE_I2C_BB_EN(hw); + /* Start condition must begin with data and clock high */ ixgbe_set_i2c_data(hw, &i2cctl, 1); ixgbe_raise_i2c_clk(hw, &i2cctl); @@ -1838,10 +2002,15 @@ static void ixgbe_i2c_start(struct ixgbe_hw *hw) * @hw: pointer to hardware structure * * Sets I2C stop condition (Low -> High on SDA while SCL is High) + * Disables bit-bang mode and negates data output enable on X550 + * hardware. **/ static void ixgbe_i2c_stop(struct ixgbe_hw *hw) { u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); + u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); + u32 clk_oe_bit = IXGBE_I2C_CLK_OE_N_EN(hw); + u32 bb_en_bit = IXGBE_I2C_BB_EN(hw); /* Stop condition must begin with data low and clock high */ ixgbe_set_i2c_data(hw, &i2cctl, 0); @@ -1854,6 +2023,13 @@ static void ixgbe_i2c_stop(struct ixgbe_hw *hw) /* bus free time between stop and start (4.7us)*/ udelay(IXGBE_I2C_T_BUF); + + if (bb_en_bit || data_oe_bit || clk_oe_bit) { + i2cctl &= ~bb_en_bit; + i2cctl |= data_oe_bit | clk_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl); + IXGBE_WRITE_FLUSH(hw); + } } /** @@ -1868,6 +2044,7 @@ static s32 ixgbe_clock_in_i2c_byte(struct ixgbe_hw *hw, u8 *data) s32 i; bool bit = false; + *data = 0; for (i = 7; i >= 0; i--) { ixgbe_clock_in_i2c_bit(hw, &bit); *data |= bit << i; @@ -1901,6 +2078,7 @@ static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data) /* Release SDA line (set high) */ i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); i2cctl |= IXGBE_I2C_DATA_OUT(hw); + i2cctl |= IXGBE_I2C_DATA_OE_N_EN(hw); IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl); IXGBE_WRITE_FLUSH(hw); @@ -1915,15 +2093,21 @@ static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data) **/ static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw) { + u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); s32 status = 0; u32 i = 0; u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); u32 timeout = 10; bool ack = true; + if (data_oe_bit) { + i2cctl |= IXGBE_I2C_DATA_OUT(hw); + i2cctl |= data_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl); + IXGBE_WRITE_FLUSH(hw); + } ixgbe_raise_i2c_clk(hw, &i2cctl); - /* Minimum high period of clock is 4us */ udelay(IXGBE_I2C_T_HIGH); @@ -1961,7 +2145,14 @@ static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw) static s32 ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data) { u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); + u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); + if (data_oe_bit) { + i2cctl |= IXGBE_I2C_DATA_OUT(hw); + i2cctl |= data_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl); + IXGBE_WRITE_FLUSH(hw); + } ixgbe_raise_i2c_clk(hw, &i2cctl); /* Minimum high period of clock is 4us */ @@ -2016,13 +2207,20 @@ static s32 ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data) * @i2cctl: Current value of I2CCTL register * * Raises the I2C clock line '0'->'1' + * Negates the I2C clock output enable on X550 hardware. **/ static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl) { + u32 clk_oe_bit = IXGBE_I2C_CLK_OE_N_EN(hw); u32 i = 0; u32 timeout = IXGBE_I2C_CLOCK_STRETCHING_TIMEOUT; u32 i2cctl_r = 0; + if (clk_oe_bit) { + *i2cctl |= clk_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); + } + for (i = 0; i < timeout; i++) { *i2cctl |= IXGBE_I2C_CLK_OUT(hw); IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); @@ -2042,11 +2240,13 @@ static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl) * @i2cctl: Current value of I2CCTL register * * Lowers the I2C clock line '1'->'0' + * Asserts the I2C clock output enable on X550 hardware. **/ static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl) { *i2cctl &= ~IXGBE_I2C_CLK_OUT(hw); + *i2cctl &= ~IXGBE_I2C_CLK_OE_N_EN(hw); IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); IXGBE_WRITE_FLUSH(hw); @@ -2062,13 +2262,17 @@ static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl) * @data: I2C data value (0 or 1) to set * * Sets the I2C data bit + * Asserts the I2C data output enable on X550 hardware. **/ static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data) { + u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); + if (data) *i2cctl |= IXGBE_I2C_DATA_OUT(hw); else *i2cctl &= ~IXGBE_I2C_DATA_OUT(hw); + *i2cctl &= ~data_oe_bit; IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); IXGBE_WRITE_FLUSH(hw); @@ -2076,6 +2280,14 @@ static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data) /* Data rise/fall (1000ns/300ns) and set-up time (250ns) */ udelay(IXGBE_I2C_T_RISE + IXGBE_I2C_T_FALL + IXGBE_I2C_T_SU_DATA); + if (!data) /* Can't verify data in this case */ + return 0; + if (data_oe_bit) { + *i2cctl |= data_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); + IXGBE_WRITE_FLUSH(hw); + } + /* Verify data was set correctly */ *i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); if (data != ixgbe_get_i2c_data(hw, i2cctl)) { @@ -2092,9 +2304,19 @@ static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data) * @i2cctl: Current value of I2CCTL register * * Returns the I2C data bit value + * Negates the I2C data output enable on X550 hardware. **/ static bool ixgbe_get_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl) { + u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); + + if (data_oe_bit) { + *i2cctl |= data_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); + IXGBE_WRITE_FLUSH(hw); + udelay(IXGBE_I2C_T_FALL); + } + if (*i2cctl & IXGBE_I2C_DATA_IN(hw)) return true; return false; @@ -2109,10 +2331,11 @@ static bool ixgbe_get_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl) **/ static void ixgbe_i2c_bus_clear(struct ixgbe_hw *hw) { - u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); + u32 i2cctl; u32 i; ixgbe_i2c_start(hw); + i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); ixgbe_set_i2c_data(hw, &i2cctl, 1); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h index e45988c4dad5..5abd66c84d00 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h @@ -66,6 +66,9 @@ #define IXGBE_SFF_1GBASET_CAPABLE 0x8 #define IXGBE_SFF_10GBASESR_CAPABLE 0x10 #define IXGBE_SFF_10GBASELR_CAPABLE 0x20 +#define IXGBE_SFF_SOFT_RS_SELECT_MASK 0x8 +#define IXGBE_SFF_SOFT_RS_SELECT_10G 0x8 +#define IXGBE_SFF_SOFT_RS_SELECT_1G 0x0 #define IXGBE_SFF_ADDRESSING_MODE 0x4 #define IXGBE_SFF_QSFP_DA_ACTIVE_CABLE 0x1 #define IXGBE_SFF_QSFP_DA_PASSIVE_CABLE 0x8 @@ -78,9 +81,29 @@ #define IXGBE_I2C_EEPROM_STATUS_FAIL 0x2 #define IXGBE_I2C_EEPROM_STATUS_IN_PROGRESS 0x3 #define IXGBE_CS4227 0xBE /* CS4227 address */ -#define IXGBE_CS4227_SPARE24_LSB 0x12B0 /* Reg to program EDC */ +#define IXGBE_CS4227_SCRATCH 2 +#define IXGBE_CS4227_RESET_PENDING 0x1357 +#define IXGBE_CS4227_RESET_COMPLETE 0x5AA5 +#define IXGBE_CS4227_RETRIES 15 +#define IXGBE_CS4227_EFUSE_STATUS 0x0181 +#define IXGBE_CS4227_LINE_SPARE22_MSB 0x12AD /* Reg to set speed */ +#define IXGBE_CS4227_LINE_SPARE24_LSB 0x12B0 /* Reg to set EDC */ +#define IXGBE_CS4227_HOST_SPARE22_MSB 0x1AAD /* Reg to set speed */ +#define IXGBE_CS4227_HOST_SPARE24_LSB 0x1AB0 /* Reg to program EDC */ +#define IXGBE_CS4227_EEPROM_STATUS 0x5001 +#define IXGBE_CS4227_EEPROM_LOAD_OK 0x0001 +#define IXGBE_CS4227_SPEED_1G 0x8000 +#define IXGBE_CS4227_SPEED_10G 0 #define IXGBE_CS4227_EDC_MODE_CX1 0x0002 #define IXGBE_CS4227_EDC_MODE_SR 0x0004 +#define IXGBE_CS4227_EDC_MODE_DIAG 0x0008 +#define IXGBE_CS4227_RESET_HOLD 500 /* microseconds */ +#define IXGBE_CS4227_RESET_DELAY 500 /* milliseconds */ +#define IXGBE_CS4227_CHECK_DELAY 30 /* milliseconds */ +#define IXGBE_PE 0xE0 /* Port expander addr */ +#define IXGBE_PE_OUTPUT 1 /* Output reg offset */ +#define IXGBE_PE_CONFIG 3 /* Config reg offset */ +#define IXGBE_PE_BIT1 (1 << 1) /* Flow control defines */ #define IXGBE_TAF_SYM_PAUSE 0x400 @@ -109,6 +132,8 @@ #define IXGBE_I2C_T_SU_STO 4 #define IXGBE_I2C_T_BUF 5 +#define IXGBE_SFP_DETECT_RETRIES 2 + #define IXGBE_TN_LASI_STATUS_REG 0x9005 #define IXGBE_TN_LASI_STATUS_TEMP_ALARM 0x0008 @@ -154,8 +179,12 @@ s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, s32 ixgbe_tn_check_overtemp(struct ixgbe_hw *hw); s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 *data); +s32 ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data); s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 data); +s32 ixgbe_write_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data); s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 *eeprom_data); s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset, @@ -164,6 +193,10 @@ s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 eeprom_data); s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, u16 reg, u16 *val); +s32 ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 *val); s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, u16 reg, u16 val); +s32 ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 val); #endif /* _IXGBE_PHY_H_ */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 63689192b149..939c90c4ff39 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -402,6 +402,7 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_FDIRSIP4M 0x0EE40 #define IXGBE_FDIRTCPM 0x0EE44 #define IXGBE_FDIRUDPM 0x0EE48 +#define IXGBE_FDIRSCTPM 0x0EE78 #define IXGBE_FDIRIP6M 0x0EE74 #define IXGBE_FDIRM 0x0EE70 @@ -1192,6 +1193,7 @@ struct ixgbe_thermal_sensor_data { /* RDRXCTL Bit Masks */ #define IXGBE_RDRXCTL_RDMTS_1_2 0x00000000 /* Rx Desc Min Threshold Size */ #define IXGBE_RDRXCTL_CRCSTRIP 0x00000002 /* CRC Strip */ +#define IXGBE_RDRXCTL_PSP 0x00000004 /* Pad small packet */ #define IXGBE_RDRXCTL_MVMEN 0x00000020 #define IXGBE_RDRXCTL_DMAIDONE 0x00000008 /* DMA init cycle done */ #define IXGBE_RDRXCTL_AGGDIS 0x00010000 /* Aggregation disable */ @@ -1948,6 +1950,7 @@ enum { #define IXGBE_GSSR_SW_MNG_SM 0x0400 #define IXGBE_GSSR_SHARED_I2C_SM 0x1806 /* Wait for both phys & I2Cs */ #define IXGBE_GSSR_I2C_MASK 0x1800 +#define IXGBE_GSSR_NVM_PHY_MASK 0xF /* FW Status register bitmask */ #define IXGBE_FWSTS_FWRI 0x00000200 /* Firmware Reset Indication */ @@ -3255,9 +3258,11 @@ struct ixgbe_mac_operations { void (*flap_tx_laser)(struct ixgbe_hw *); void (*stop_link_on_d3)(struct ixgbe_hw *); s32 (*setup_link)(struct ixgbe_hw *, ixgbe_link_speed, bool); + s32 (*setup_mac_link)(struct ixgbe_hw *, ixgbe_link_speed, bool); s32 (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *, bool); s32 (*get_link_capabilities)(struct ixgbe_hw *, ixgbe_link_speed *, bool *); + void (*set_rate_select_speed)(struct ixgbe_hw *, ixgbe_link_speed); /* Packet Buffer Manipulation */ void (*set_rxpba)(struct ixgbe_hw *, int, u32, int); @@ -3328,6 +3333,10 @@ struct ixgbe_phy_operations { s32 (*set_phy_power)(struct ixgbe_hw *, bool on); s32 (*enter_lplu)(struct ixgbe_hw *); s32 (*handle_lasi)(struct ixgbe_hw *hw); + s32 (*read_i2c_combined_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, + u16 *value); + s32 (*write_i2c_combined_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, + u16 value); }; struct ixgbe_eeprom_info { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index 4e758435ece8..c1d4584f6469 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -567,19 +567,25 @@ static s32 ixgbe_poll_flash_update_done_X540(struct ixgbe_hw *hw) **/ s32 ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) { - u32 swfw_sync; - u32 swmask = mask; - u32 fwmask = mask << 5; - u32 hwmask = 0; + u32 swmask = mask & IXGBE_GSSR_NVM_PHY_MASK; + u32 swi2c_mask = mask & IXGBE_GSSR_I2C_MASK; + u32 fwmask = swmask << 5; u32 timeout = 200; + u32 hwmask = 0; + u32 swfw_sync; u32 i; - if (swmask == IXGBE_GSSR_EEP_SM) + if (swmask & IXGBE_GSSR_EEP_SM) hwmask = IXGBE_GSSR_FLASH_SM; + /* SW only mask does not have FW bit pair */ + if (mask & IXGBE_GSSR_SW_MNG_SM) + swmask |= IXGBE_GSSR_SW_MNG_SM; + + swmask |= swi2c_mask; + fwmask |= swi2c_mask << 2; for (i = 0; i < timeout; i++) { - /* - * SW NVM semaphore bit is used for access to all + /* SW NVM semaphore bit is used for access to all * SW_FW_SYNC bits (not just NVM) */ if (ixgbe_get_swfw_sync_semaphore(hw)) @@ -590,39 +596,56 @@ s32 ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) swfw_sync |= swmask; IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync); ixgbe_release_swfw_sync_semaphore(hw); - break; - } else { - /* - * Firmware currently using resource (fwmask), - * hardware currently using resource (hwmask), - * or other software thread currently using - * resource (swmask) - */ - ixgbe_release_swfw_sync_semaphore(hw); - usleep_range(5000, 10000); + usleep_range(5000, 6000); + return 0; } + /* Firmware currently using resource (fwmask), hardware + * currently using resource (hwmask), or other software + * thread currently using resource (swmask) + */ + ixgbe_release_swfw_sync_semaphore(hw); + usleep_range(5000, 10000); } - /* - * If the resource is not released by the FW/HW the SW can assume that - * the FW/HW malfunctions. In that case the SW should sets the - * SW bit(s) of the requested resource(s) while ignoring the - * corresponding FW/HW bits in the SW_FW_SYNC register. - */ - if (i >= timeout) { - swfw_sync = IXGBE_READ_REG(hw, IXGBE_SWFW_SYNC(hw)); - if (swfw_sync & (fwmask | hwmask)) { - if (ixgbe_get_swfw_sync_semaphore(hw)) - return IXGBE_ERR_SWFW_SYNC; + /* Failed to get SW only semaphore */ + if (swmask == IXGBE_GSSR_SW_MNG_SM) { + hw_dbg(hw, "Failed to get SW only semaphore\n"); + return IXGBE_ERR_SWFW_SYNC; + } - swfw_sync |= swmask; - IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync); - ixgbe_release_swfw_sync_semaphore(hw); - } + /* If the resource is not released by the FW/HW the SW can assume that + * the FW/HW malfunctions. In that case the SW should set the SW bit(s) + * of the requested resource(s) while ignoring the corresponding FW/HW + * bits in the SW_FW_SYNC register. + */ + if (ixgbe_get_swfw_sync_semaphore(hw)) + return IXGBE_ERR_SWFW_SYNC; + swfw_sync = IXGBE_READ_REG(hw, IXGBE_SWFW_SYNC(hw)); + if (swfw_sync & (fwmask | hwmask)) { + swfw_sync |= swmask; + IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync); + ixgbe_release_swfw_sync_semaphore(hw); + usleep_range(5000, 6000); + return 0; } + /* If the resource is not released by other SW the SW can assume that + * the other SW malfunctions. In that case the SW should clear all SW + * flags that it does not own and then repeat the whole process once + * again. + */ + if (swfw_sync & swmask) { + u32 rmask = IXGBE_GSSR_EEP_SM | IXGBE_GSSR_PHY0_SM | + IXGBE_GSSR_PHY1_SM | IXGBE_GSSR_MAC_CSR_SM; + + if (swi2c_mask) + rmask |= IXGBE_GSSR_I2C_MASK; + ixgbe_release_swfw_sync_X540(hw, rmask); + ixgbe_release_swfw_sync_semaphore(hw); + return IXGBE_ERR_SWFW_SYNC; + } + ixgbe_release_swfw_sync_semaphore(hw); - usleep_range(5000, 10000); - return 0; + return IXGBE_ERR_SWFW_SYNC; } /** @@ -635,9 +658,11 @@ s32 ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) **/ void ixgbe_release_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) { + u32 swmask = mask & (IXGBE_GSSR_NVM_PHY_MASK | IXGBE_GSSR_SW_MNG_SM); u32 swfw_sync; - u32 swmask = mask; + if (mask & IXGBE_GSSR_I2C_MASK) + swmask |= mask & IXGBE_GSSR_I2C_MASK; ixgbe_get_swfw_sync_semaphore(hw); swfw_sync = IXGBE_READ_REG(hw, IXGBE_SWFW_SYNC(hw)); @@ -645,7 +670,7 @@ void ixgbe_release_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync); ixgbe_release_swfw_sync_semaphore(hw); - usleep_range(5000, 10000); + usleep_range(5000, 6000); } /** @@ -686,6 +711,11 @@ static s32 ixgbe_get_swfw_sync_semaphore(struct ixgbe_hw *hw) usleep_range(50, 100); } + /* Release semaphores and return error if SW NVM semaphore + * was not granted because we do not have access to the EEPROM + */ + hw_dbg(hw, "REGSMP Software NVM semaphore not granted\n"); + ixgbe_release_swfw_sync_semaphore(hw); return IXGBE_ERR_EEPROM; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 9fe9445cd73b..ed7b2899affe 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -56,6 +56,283 @@ static void ixgbe_setup_mux_ctl(struct ixgbe_hw *hw) IXGBE_WRITE_FLUSH(hw); } +/** + * ixgbe_read_cs4227 - Read CS4227 register + * @hw: pointer to hardware structure + * @reg: register number to write + * @value: pointer to receive value read + * + * Returns status code + */ +static s32 ixgbe_read_cs4227(struct ixgbe_hw *hw, u16 reg, u16 *value) +{ + return hw->phy.ops.read_i2c_combined_unlocked(hw, IXGBE_CS4227, reg, + value); +} + +/** + * ixgbe_write_cs4227 - Write CS4227 register + * @hw: pointer to hardware structure + * @reg: register number to write + * @value: value to write to register + * + * Returns status code + */ +static s32 ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value) +{ + return hw->phy.ops.write_i2c_combined_unlocked(hw, IXGBE_CS4227, reg, + value); +} + +/** + * ixgbe_check_cs4227_reg - Perform diag on a CS4227 register + * @hw: pointer to hardware structure + * @reg: the register to check + * + * Performs a diagnostic on a register in the CS4227 chip. Returns an error + * if it is not operating correctly. + * This function assumes that the caller has acquired the proper semaphore. + */ +static s32 ixgbe_check_cs4227_reg(struct ixgbe_hw *hw, u16 reg) +{ + s32 status; + u32 retry; + u16 reg_val; + + reg_val = (IXGBE_CS4227_EDC_MODE_DIAG << 1) | 1; + status = ixgbe_write_cs4227(hw, reg, reg_val); + if (status) + return status; + for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) { + msleep(IXGBE_CS4227_CHECK_DELAY); + reg_val = 0xFFFF; + ixgbe_read_cs4227(hw, reg, ®_val); + if (!reg_val) + break; + } + if (reg_val) { + hw_err(hw, "CS4227 reg 0x%04X failed diagnostic\n", reg); + return status; + } + + return 0; +} + +/** + * ixgbe_get_cs4227_status - Return CS4227 status + * @hw: pointer to hardware structure + * + * Performs a diagnostic on the CS4227 chip. Returns an error if it is + * not operating correctly. + * This function assumes that the caller has acquired the proper semaphore. + */ +static s32 ixgbe_get_cs4227_status(struct ixgbe_hw *hw) +{ + s32 status; + u16 value = 0; + + /* Exit if the diagnostic has already been performed. */ + status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value); + if (status) + return status; + if (value == IXGBE_CS4227_RESET_COMPLETE) + return 0; + + /* Check port 0. */ + status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB); + if (status) + return status; + + status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB); + if (status) + return status; + + /* Check port 1. */ + status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB + + (1 << 12)); + if (status) + return status; + + return ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB + + (1 << 12)); +} + +/** + * ixgbe_read_pe - Read register from port expander + * @hw: pointer to hardware structure + * @reg: register number to read + * @value: pointer to receive read value + * + * Returns status code + */ +static s32 ixgbe_read_pe(struct ixgbe_hw *hw, u8 reg, u8 *value) +{ + s32 status; + + status = ixgbe_read_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE, value); + if (status) + hw_err(hw, "port expander access failed with %d\n", status); + return status; +} + +/** + * ixgbe_write_pe - Write register to port expander + * @hw: pointer to hardware structure + * @reg: register number to write + * @value: value to write + * + * Returns status code + */ +static s32 ixgbe_write_pe(struct ixgbe_hw *hw, u8 reg, u8 value) +{ + s32 status; + + status = ixgbe_write_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE, + value); + if (status) + hw_err(hw, "port expander access failed with %d\n", status); + return status; +} + +/** + * ixgbe_reset_cs4227 - Reset CS4227 using port expander + * @hw: pointer to hardware structure + * + * Returns error code + */ +static s32 ixgbe_reset_cs4227(struct ixgbe_hw *hw) +{ + s32 status; + u32 retry; + u16 value; + u8 reg; + + /* Trigger hard reset. */ + status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, ®); + if (status) + return status; + reg |= IXGBE_PE_BIT1; + status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg); + if (status) + return status; + + status = ixgbe_read_pe(hw, IXGBE_PE_CONFIG, ®); + if (status) + return status; + reg &= ~IXGBE_PE_BIT1; + status = ixgbe_write_pe(hw, IXGBE_PE_CONFIG, reg); + if (status) + return status; + + status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, ®); + if (status) + return status; + reg &= ~IXGBE_PE_BIT1; + status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg); + if (status) + return status; + + usleep_range(IXGBE_CS4227_RESET_HOLD, IXGBE_CS4227_RESET_HOLD + 100); + + status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, ®); + if (status) + return status; + reg |= IXGBE_PE_BIT1; + status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg); + if (status) + return status; + + /* Wait for the reset to complete. */ + msleep(IXGBE_CS4227_RESET_DELAY); + for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) { + status = ixgbe_read_cs4227(hw, IXGBE_CS4227_EFUSE_STATUS, + &value); + if (!status && value == IXGBE_CS4227_EEPROM_LOAD_OK) + break; + msleep(IXGBE_CS4227_CHECK_DELAY); + } + if (retry == IXGBE_CS4227_RETRIES) { + hw_err(hw, "CS4227 reset did not complete\n"); + return IXGBE_ERR_PHY; + } + + status = ixgbe_read_cs4227(hw, IXGBE_CS4227_EEPROM_STATUS, &value); + if (status || !(value & IXGBE_CS4227_EEPROM_LOAD_OK)) { + hw_err(hw, "CS4227 EEPROM did not load successfully\n"); + return IXGBE_ERR_PHY; + } + + return 0; +} + +/** + * ixgbe_check_cs4227 - Check CS4227 and reset as needed + * @hw: pointer to hardware structure + */ +static void ixgbe_check_cs4227(struct ixgbe_hw *hw) +{ + u32 swfw_mask = hw->phy.phy_semaphore_mask; + s32 status; + u16 value; + u8 retry; + + for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) { + status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask); + if (status) { + hw_err(hw, "semaphore failed with %d\n", status); + msleep(IXGBE_CS4227_CHECK_DELAY); + continue; + } + + /* Get status of reset flow. */ + status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value); + if (!status && value == IXGBE_CS4227_RESET_COMPLETE) + goto out; + + if (status || value != IXGBE_CS4227_RESET_PENDING) + break; + + /* Reset is pending. Wait and check again. */ + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + msleep(IXGBE_CS4227_CHECK_DELAY); + } + + /* Reset the CS4227. */ + status = ixgbe_reset_cs4227(hw); + if (status) { + hw_err(hw, "CS4227 reset failed: %d", status); + goto out; + } + + /* Reset takes so long, temporarily release semaphore in case the + * other driver instance is waiting for the reset indication. + */ + ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH, + IXGBE_CS4227_RESET_PENDING); + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + usleep_range(10000, 12000); + status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask); + if (status) { + hw_err(hw, "semaphore failed with %d", status); + return; + } + + /* Is the CS4227 working correctly? */ + status = ixgbe_get_cs4227_status(hw); + if (status) { + hw_err(hw, "CS4227 status failed: %d", status); + goto out; + } + + /* Record completion for next time. */ + status = ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH, + IXGBE_CS4227_RESET_COMPLETE); + +out: + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + msleep(hw->eeprom.semaphore_delay); +} + /** ixgbe_identify_phy_x550em - Get PHY type based on device id * @hw: pointer to hardware structure * @@ -68,7 +345,7 @@ static s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw) /* set up for CS4227 usage */ hw->phy.phy_semaphore_mask = IXGBE_GSSR_SHARED_I2C_SM; ixgbe_setup_mux_ctl(hw); - + ixgbe_check_cs4227(hw); return ixgbe_identify_module_generic(hw); case IXGBE_DEV_ID_X550EM_X_KX4: hw->phy.type = ixgbe_phy_x550em_kx4; @@ -910,6 +1187,96 @@ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) } /** + * ixgbe_supported_sfp_modules_X550em - Check if SFP module type is supported + * @hw: pointer to hardware structure + * @linear: true if SFP module is linear + */ +static s32 ixgbe_supported_sfp_modules_X550em(struct ixgbe_hw *hw, bool *linear) +{ + switch (hw->phy.sfp_type) { + case ixgbe_sfp_type_not_present: + return IXGBE_ERR_SFP_NOT_PRESENT; + case ixgbe_sfp_type_da_cu_core0: + case ixgbe_sfp_type_da_cu_core1: + *linear = true; + break; + case ixgbe_sfp_type_srlr_core0: + case ixgbe_sfp_type_srlr_core1: + case ixgbe_sfp_type_da_act_lmt_core0: + case ixgbe_sfp_type_da_act_lmt_core1: + case ixgbe_sfp_type_1g_sx_core0: + case ixgbe_sfp_type_1g_sx_core1: + case ixgbe_sfp_type_1g_lx_core0: + case ixgbe_sfp_type_1g_lx_core1: + *linear = false; + break; + case ixgbe_sfp_type_unknown: + case ixgbe_sfp_type_1g_cu_core0: + case ixgbe_sfp_type_1g_cu_core1: + default: + return IXGBE_ERR_SFP_NOT_SUPPORTED; + } + + return 0; +} + +/** + * ixgbe_setup_mac_link_sfp_x550em - Configure the KR PHY for SFP. + * @hw: pointer to hardware structure + * + * Configures the extern PHY and the integrated KR PHY for SFP support. + */ +static s32 +ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw, + ixgbe_link_speed speed, + __always_unused bool autoneg_wait_to_complete) +{ + s32 status; + u16 slice, value; + bool setup_linear = false; + + /* Check if SFP module is supported and linear */ + status = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear); + + /* If no SFP module present, then return success. Return success since + * there is no reason to configure CS4227 and SFP not present error is + * not accepted in the setup MAC link flow. + */ + if (status == IXGBE_ERR_SFP_NOT_PRESENT) + return 0; + + if (status) + return status; + + /* Configure CS4227 LINE side to 10G SR. */ + slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12); + value = IXGBE_CS4227_SPEED_10G; + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice, + value); + + /* Configure CS4227 for HOST connection rate then type. */ + slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12); + value = speed & IXGBE_LINK_SPEED_10GB_FULL ? + IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G; + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice, + value); + + slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12); + if (setup_linear) + value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1; + else + value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1; + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice, + value); + + /* If internal link mode is XFI, then setup XFI internal link. */ + if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) + status = ixgbe_setup_ixfi_x550em(hw, &speed); + + return status; +} + +/** * ixgbe_setup_mac_link_t_X550em - Sets the auto advertised link speed * @hw: pointer to hardware structure * @speed: new link speed @@ -1003,6 +1370,10 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw) mac->ops.disable_tx_laser = NULL; mac->ops.enable_tx_laser = NULL; mac->ops.flap_tx_laser = NULL; + mac->ops.setup_link = ixgbe_setup_mac_link_multispeed_fiber; + mac->ops.setup_mac_link = ixgbe_setup_mac_link_sfp_x550em; + mac->ops.set_rate_select_speed = + ixgbe_set_soft_rate_select_speed; break; case ixgbe_media_type_copper: mac->ops.setup_link = ixgbe_setup_mac_link_t_X550em; @@ -1018,53 +1389,18 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw) */ static s32 ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw) { - bool setup_linear; - u16 reg_slice, edc_mode; - s32 ret_val; + s32 status; + bool linear; - switch (hw->phy.sfp_type) { - case ixgbe_sfp_type_unknown: - return 0; - case ixgbe_sfp_type_not_present: - return IXGBE_ERR_SFP_NOT_PRESENT; - case ixgbe_sfp_type_da_cu_core0: - case ixgbe_sfp_type_da_cu_core1: - setup_linear = true; - break; - case ixgbe_sfp_type_srlr_core0: - case ixgbe_sfp_type_srlr_core1: - case ixgbe_sfp_type_da_act_lmt_core0: - case ixgbe_sfp_type_da_act_lmt_core1: - case ixgbe_sfp_type_1g_sx_core0: - case ixgbe_sfp_type_1g_sx_core1: - setup_linear = false; - break; - default: - return IXGBE_ERR_SFP_NOT_SUPPORTED; - } + /* Check if SFP module is supported */ + status = ixgbe_supported_sfp_modules_X550em(hw, &linear); + if (status) + return status; ixgbe_init_mac_link_ops_X550em(hw); hw->phy.ops.reset = NULL; - /* The CS4227 slice address is the base address + the port-pair reg - * offset. I.e. Slice 0 = 0x12B0 and slice 1 = 0x22B0. - */ - reg_slice = IXGBE_CS4227_SPARE24_LSB + (hw->bus.lan_id << 12); - - if (setup_linear) - edc_mode = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 0x1; - else - edc_mode = (IXGBE_CS4227_EDC_MODE_SR << 1) | 0x1; - - /* Configure CS4227 for connection type. */ - ret_val = hw->phy.ops.write_i2c_combined(hw, IXGBE_CS4227, reg_slice, - edc_mode); - - if (ret_val) - ret_val = hw->phy.ops.write_i2c_combined(hw, 0x80, reg_slice, - edc_mode); - - return ret_val; + return 0; } /** ixgbe_get_link_capabilities_x550em - Determines link capabilities @@ -1927,6 +2263,62 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw, IXGBE_WRITE_REG(hw, IXGBE_PFFLPH, (u32)(pfflp >> 32)); } +/** + * ixgbe_set_mux - Set mux for port 1 access with CS4227 + * @hw: pointer to hardware structure + * @state: set mux if 1, clear if 0 + */ +static void ixgbe_set_mux(struct ixgbe_hw *hw, u8 state) +{ + u32 esdp; + + if (!hw->bus.lan_id) + return; + esdp = IXGBE_READ_REG(hw, IXGBE_ESDP); + if (state) + esdp |= IXGBE_ESDP_SDP1; + else + esdp &= ~IXGBE_ESDP_SDP1; + IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); + IXGBE_WRITE_FLUSH(hw); +} + +/** + * ixgbe_acquire_swfw_sync_X550em - Acquire SWFW semaphore + * @hw: pointer to hardware structure + * @mask: Mask to specify which semaphore to acquire + * + * Acquires the SWFW semaphore and sets the I2C MUX + */ +static s32 ixgbe_acquire_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask) +{ + s32 status; + + status = ixgbe_acquire_swfw_sync_X540(hw, mask); + if (status) + return status; + + if (mask & IXGBE_GSSR_I2C_MASK) + ixgbe_set_mux(hw, 1); + + return 0; +} + +/** + * ixgbe_release_swfw_sync_X550em - Release SWFW semaphore + * @hw: pointer to hardware structure + * @mask: Mask to specify which semaphore to release + * + * Releases the SWFW semaphore and sets the I2C MUX + */ +static void ixgbe_release_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask) +{ + if (mask & IXGBE_GSSR_I2C_MASK) + ixgbe_set_mux(hw, 0); + + ixgbe_release_swfw_sync_X540(hw, mask); +} + #define X550_COMMON_MAC \ .init_hw = &ixgbe_init_hw_generic, \ .start_hw = &ixgbe_start_hw_X540, \ @@ -1964,8 +2356,6 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw, &ixgbe_set_source_address_pruning_X550, \ .set_ethertype_anti_spoofing = \ &ixgbe_set_ethertype_anti_spoofing_X550, \ - .acquire_swfw_sync = &ixgbe_acquire_swfw_sync_X540, \ - .release_swfw_sync = &ixgbe_release_swfw_sync_X540, \ .disable_rx_buff = &ixgbe_disable_rx_buff_generic, \ .enable_rx_buff = &ixgbe_enable_rx_buff_generic, \ .get_thermal_sensor_data = NULL, \ @@ -1985,6 +2375,8 @@ static struct ixgbe_mac_operations mac_ops_X550 = { .get_link_capabilities = &ixgbe_get_copper_link_capabilities_generic, .get_bus_info = &ixgbe_get_bus_info_generic, .setup_sfp = NULL, + .acquire_swfw_sync = &ixgbe_acquire_swfw_sync_X540, + .release_swfw_sync = &ixgbe_release_swfw_sync_X540, }; static struct ixgbe_mac_operations mac_ops_X550EM_x = { @@ -1997,7 +2389,8 @@ static struct ixgbe_mac_operations mac_ops_X550EM_x = { .get_link_capabilities = &ixgbe_get_link_capabilities_X550em, .get_bus_info = &ixgbe_get_bus_info_X550em, .setup_sfp = ixgbe_setup_sfp_modules_X550em, - + .acquire_swfw_sync = &ixgbe_acquire_swfw_sync_X550em, + .release_swfw_sync = &ixgbe_release_swfw_sync_X550em, }; #define X550_COMMON_EEP \ @@ -2039,14 +2432,17 @@ static struct ixgbe_phy_operations phy_ops_X550 = { X550_COMMON_PHY .init = NULL, .identify = &ixgbe_identify_phy_generic, - .read_i2c_combined = &ixgbe_read_i2c_combined_generic, - .write_i2c_combined = &ixgbe_write_i2c_combined_generic, }; static struct ixgbe_phy_operations phy_ops_X550EM_x = { X550_COMMON_PHY .init = &ixgbe_init_phy_ops_X550em, .identify = &ixgbe_identify_phy_x550em, + .read_i2c_combined = &ixgbe_read_i2c_combined_generic, + .write_i2c_combined = &ixgbe_write_i2c_combined_generic, + .read_i2c_combined_unlocked = &ixgbe_read_i2c_combined_generic_unlocked, + .write_i2c_combined_unlocked = + &ixgbe_write_i2c_combined_generic_unlocked, }; static const u32 ixgbe_mvals_X550[IXGBE_MVALS_IDX_LIMIT] = { diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 149a0b4489be..35da2d74e73e 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -3896,6 +3896,7 @@ static const struct net_device_ops ixgbevf_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ixgbevf_netpoll, #endif + .ndo_features_check = passthru_features_check, }; static void ixgbevf_assign_netdev_ops(struct net_device *dev) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index fe2299ac4f5c..dd6fe942acf9 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -32,6 +32,7 @@ #include <linux/of_address.h> #include <linux/phy.h> #include <linux/clk.h> +#include <linux/cpu.h> /* Registers */ #define MVNETA_RXQ_CONFIG_REG(q) (0x1400 + ((q) << 2)) @@ -285,23 +286,34 @@ struct mvneta_pcpu_stats { u64 tx_bytes; }; +struct mvneta_pcpu_port { + /* Pointer to the shared port */ + struct mvneta_port *pp; + + /* Pointer to the CPU-local NAPI struct */ + struct napi_struct napi; + + /* Cause of the previous interrupt */ + u32 cause_rx_tx; +}; + struct mvneta_port { + struct mvneta_pcpu_port __percpu *ports; + struct mvneta_pcpu_stats __percpu *stats; + int pkt_size; unsigned int frag_size; void __iomem *base; struct mvneta_rx_queue *rxqs; struct mvneta_tx_queue *txqs; struct net_device *dev; - - u32 cause_rx_tx; - struct napi_struct napi; + struct notifier_block cpu_notifier; /* Core clock */ struct clk *clk; u8 mcast_count[256]; u16 tx_ring_size; u16 rx_ring_size; - struct mvneta_pcpu_stats *stats; struct mii_bus *mii_bus; struct phy_device *phy_dev; @@ -468,7 +480,7 @@ struct mvneta_rx_queue { /* The hardware supports eight (8) rx queues, but we are only allowing * the first one to be used. Therefore, let's just allocate one queue. */ -static int rxq_number = 1; +static int rxq_number = 8; static int txq_number = 8; static int rxq_def; @@ -756,14 +768,7 @@ static void mvneta_port_up(struct mvneta_port *pp) mvreg_write(pp, MVNETA_TXQ_CMD, q_map); /* Enable all initialized RXQs. */ - q_map = 0; - for (queue = 0; queue < rxq_number; queue++) { - struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; - if (rxq->descs != NULL) - q_map |= (1 << queue); - } - - mvreg_write(pp, MVNETA_RXQ_CMD, q_map); + mvreg_write(pp, MVNETA_RXQ_CMD, BIT(rxq_def)); } /* Stop the Ethernet port activity */ @@ -949,7 +954,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp) /* Set CPU queue access map - all CPUs have access to all RX * queues and to all TX queues */ - for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) + for_each_present_cpu(cpu) mvreg_write(pp, MVNETA_CPU_MAP(cpu), (MVNETA_CPU_RXQ_ACCESS_ALL_MASK | MVNETA_CPU_TXQ_ACCESS_ALL_MASK)); @@ -1426,17 +1431,6 @@ static u32 mvneta_skb_tx_csum(struct mvneta_port *pp, struct sk_buff *skb) return MVNETA_TX_L4_CSUM_NOT; } -/* Returns rx queue pointer (find last set bit) according to causeRxTx - * value - */ -static struct mvneta_rx_queue *mvneta_rx_policy(struct mvneta_port *pp, - u32 cause) -{ - int queue = fls(cause >> 8) - 1; - - return (queue < 0 || queue >= rxq_number) ? NULL : &pp->rxqs[queue]; -} - /* Drop packets received by the RXQ and free buffers */ static void mvneta_rxq_drop_pkts(struct mvneta_port *pp, struct mvneta_rx_queue *rxq) @@ -1461,6 +1455,7 @@ static void mvneta_rxq_drop_pkts(struct mvneta_port *pp, static int mvneta_rx(struct mvneta_port *pp, int rx_todo, struct mvneta_rx_queue *rxq) { + struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports); struct net_device *dev = pp->dev; int rx_done; u32 rcvd_pkts = 0; @@ -1479,6 +1474,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo, struct mvneta_rx_desc *rx_desc = mvneta_rxq_next_desc_get(rxq); struct sk_buff *skb; unsigned char *data; + dma_addr_t phys_addr; u32 rx_status; int rx_bytes, err; @@ -1486,6 +1482,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo, rx_status = rx_desc->status; rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE); data = (unsigned char *)rx_desc->buf_cookie; + phys_addr = rx_desc->buf_phys_addr; if (!mvneta_rxq_desc_is_first_last(rx_status) || (rx_status & MVNETA_RXD_ERR_SUMMARY)) { @@ -1513,7 +1510,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo, skb->protocol = eth_type_trans(skb, dev); mvneta_rx_csum(pp, rx_status, skb); - napi_gro_receive(&pp->napi, skb); + napi_gro_receive(&port->napi, skb); rcvd_pkts++; rcvd_bytes += rx_bytes; @@ -1534,7 +1531,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo, if (!skb) goto err_drop_frame; - dma_unmap_single(dev->dev.parent, rx_desc->buf_phys_addr, + dma_unmap_single(dev->dev.parent, phys_addr, MVNETA_RX_BUF_SIZE(pp->pkt_size), DMA_FROM_DEVICE); rcvd_pkts++; @@ -1548,7 +1545,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo, mvneta_rx_csum(pp, rx_status, skb); - napi_gro_receive(&pp->napi, skb); + napi_gro_receive(&port->napi, skb); } if (rcvd_pkts) { @@ -2059,12 +2056,10 @@ static void mvneta_set_rx_mode(struct net_device *dev) /* Interrupt handling - the callback for request_irq() */ static irqreturn_t mvneta_isr(int irq, void *dev_id) { - struct mvneta_port *pp = (struct mvneta_port *)dev_id; + struct mvneta_pcpu_port *port = (struct mvneta_pcpu_port *)dev_id; - /* Mask all interrupts */ - mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); - - napi_schedule(&pp->napi); + disable_percpu_irq(port->pp->dev->irq); + napi_schedule(&port->napi); return IRQ_HANDLED; } @@ -2102,11 +2097,11 @@ static int mvneta_poll(struct napi_struct *napi, int budget) { int rx_done = 0; u32 cause_rx_tx; - unsigned long flags; struct mvneta_port *pp = netdev_priv(napi->dev); + struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports); if (!netif_running(pp->dev)) { - napi_complete(napi); + napi_complete(&port->napi); return rx_done; } @@ -2133,47 +2128,17 @@ static int mvneta_poll(struct napi_struct *napi, int budget) /* For the case where the last mvneta_poll did not process all * RX packets */ - cause_rx_tx |= pp->cause_rx_tx; - if (rxq_number > 1) { - while ((cause_rx_tx & MVNETA_RX_INTR_MASK_ALL) && (budget > 0)) { - int count; - struct mvneta_rx_queue *rxq; - /* get rx queue number from cause_rx_tx */ - rxq = mvneta_rx_policy(pp, cause_rx_tx); - if (!rxq) - break; - - /* process the packet in that rx queue */ - count = mvneta_rx(pp, budget, rxq); - rx_done += count; - budget -= count; - if (budget > 0) { - /* set off the rx bit of the - * corresponding bit in the cause rx - * tx register, so that next iteration - * will find the next rx queue where - * packets are received on - */ - cause_rx_tx &= ~((1 << rxq->id) << 8); - } - } - } else { - rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]); - budget -= rx_done; - } + cause_rx_tx |= port->cause_rx_tx; + rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]); + budget -= rx_done; if (budget > 0) { cause_rx_tx = 0; - 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_MISCINTR_INTR_MASK); - local_irq_restore(flags); + napi_complete(&port->napi); + enable_percpu_irq(pp->dev->irq, 0); } - pp->cause_rx_tx = cause_rx_tx; + port->cause_rx_tx = cause_rx_tx; return rx_done; } @@ -2377,26 +2342,19 @@ static void mvneta_cleanup_txqs(struct mvneta_port *pp) /* Cleanup all Rx queues */ static void mvneta_cleanup_rxqs(struct mvneta_port *pp) { - int queue; - - for (queue = 0; queue < rxq_number; queue++) - mvneta_rxq_deinit(pp, &pp->rxqs[queue]); + mvneta_rxq_deinit(pp, &pp->rxqs[rxq_def]); } /* Init all Rx queues */ static int mvneta_setup_rxqs(struct mvneta_port *pp) { - int queue; - - for (queue = 0; queue < rxq_number; queue++) { - int err = mvneta_rxq_init(pp, &pp->rxqs[queue]); - if (err) { - netdev_err(pp->dev, "%s: can't create rxq=%d\n", - __func__, queue); - mvneta_cleanup_rxqs(pp); - return err; - } + int err = mvneta_rxq_init(pp, &pp->rxqs[rxq_def]); + if (err) { + netdev_err(pp->dev, "%s: can't create rxq=%d\n", + __func__, rxq_def); + mvneta_cleanup_rxqs(pp); + return err; } return 0; @@ -2422,6 +2380,8 @@ static int mvneta_setup_txqs(struct mvneta_port *pp) static void mvneta_start_dev(struct mvneta_port *pp) { + unsigned int cpu; + mvneta_max_rx_size_set(pp, pp->pkt_size); mvneta_txq_max_tx_size_set(pp, pp->pkt_size); @@ -2429,7 +2389,11 @@ static void mvneta_start_dev(struct mvneta_port *pp) mvneta_port_enable(pp); /* Enable polling on the port */ - napi_enable(&pp->napi); + for_each_present_cpu(cpu) { + struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); + + napi_enable(&port->napi); + } /* Unmask interrupts */ mvreg_write(pp, MVNETA_INTR_NEW_MASK, @@ -2447,9 +2411,15 @@ static void mvneta_start_dev(struct mvneta_port *pp) static void mvneta_stop_dev(struct mvneta_port *pp) { + unsigned int cpu; + phy_stop(pp->phy_dev); - napi_disable(&pp->napi); + for_each_present_cpu(cpu) { + struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); + + napi_disable(&port->napi); + } netif_carrier_off(pp->dev); @@ -2689,6 +2659,125 @@ static void mvneta_mdio_remove(struct mvneta_port *pp) pp->phy_dev = NULL; } +static void mvneta_percpu_enable(void *arg) +{ + struct mvneta_port *pp = arg; + + enable_percpu_irq(pp->dev->irq, IRQ_TYPE_NONE); +} + +static void mvneta_percpu_disable(void *arg) +{ + struct mvneta_port *pp = arg; + + disable_percpu_irq(pp->dev->irq); +} + +static void mvneta_percpu_elect(struct mvneta_port *pp) +{ + int online_cpu_idx, cpu, i = 0; + + online_cpu_idx = rxq_def % num_online_cpus(); + + for_each_online_cpu(cpu) { + if (i == online_cpu_idx) + /* Enable per-CPU interrupt on the one CPU we + * just elected + */ + smp_call_function_single(cpu, mvneta_percpu_enable, + pp, true); + else + /* Disable per-CPU interrupt on all the other CPU */ + smp_call_function_single(cpu, mvneta_percpu_disable, + pp, true); + i++; + } +}; + +static int mvneta_percpu_notifier(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + struct mvneta_port *pp = container_of(nfb, struct mvneta_port, + cpu_notifier); + int cpu = (unsigned long)hcpu, other_cpu; + struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); + + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + netif_tx_stop_all_queues(pp->dev); + + /* We have to synchronise on tha napi of each CPU + * except the one just being waked up + */ + for_each_online_cpu(other_cpu) { + if (other_cpu != cpu) { + struct mvneta_pcpu_port *other_port = + per_cpu_ptr(pp->ports, other_cpu); + + napi_synchronize(&other_port->napi); + } + } + + /* Mask all ethernet port interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); + mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0); + napi_enable(&port->napi); + + /* Enable per-CPU interrupt on the one CPU we care + * about. + */ + mvneta_percpu_elect(pp); + + /* Unmask all ethernet port interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, + 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); + netif_tx_start_all_queues(pp->dev); + break; + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + netif_tx_stop_all_queues(pp->dev); + /* Mask all ethernet port interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); + mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0); + + napi_synchronize(&port->napi); + napi_disable(&port->napi); + /* Disable per-CPU interrupts on the CPU that is + * brought down. + */ + smp_call_function_single(cpu, mvneta_percpu_disable, + pp, true); + + break; + case CPU_DEAD: + case CPU_DEAD_FROZEN: + /* Check if a new CPU must be elected now this on is down */ + mvneta_percpu_elect(pp); + /* Unmask all ethernet port interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, + 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); + netif_tx_start_all_queues(pp->dev); + break; + } + + return NOTIFY_OK; +} + static int mvneta_open(struct net_device *dev) { struct mvneta_port *pp = netdev_priv(dev); @@ -2707,13 +2796,29 @@ static int mvneta_open(struct net_device *dev) goto err_cleanup_rxqs; /* Connect to port interrupt line */ - ret = request_irq(pp->dev->irq, mvneta_isr, 0, - MVNETA_DRIVER_NAME, pp); + ret = request_percpu_irq(pp->dev->irq, mvneta_isr, + MVNETA_DRIVER_NAME, pp->ports); if (ret) { netdev_err(pp->dev, "cannot request irq %d\n", pp->dev->irq); goto err_cleanup_txqs; } + /* Even though the documentation says that request_percpu_irq + * doesn't enable the interrupts automatically, it actually + * does so on the local CPU. + * + * Make sure it's disabled. + */ + mvneta_percpu_disable(pp); + + /* Elect a CPU to handle our RX queue interrupt */ + mvneta_percpu_elect(pp); + + /* Register a CPU notifier to handle the case where our CPU + * might be taken offline. + */ + register_cpu_notifier(&pp->cpu_notifier); + /* In default link is down */ netif_carrier_off(pp->dev); @@ -2728,7 +2833,7 @@ static int mvneta_open(struct net_device *dev) return 0; err_free_irq: - free_irq(pp->dev->irq, pp); + free_percpu_irq(pp->dev->irq, pp->ports); err_cleanup_txqs: mvneta_cleanup_txqs(pp); err_cleanup_rxqs: @@ -2740,10 +2845,14 @@ err_cleanup_rxqs: static int mvneta_stop(struct net_device *dev) { struct mvneta_port *pp = netdev_priv(dev); + int cpu; mvneta_stop_dev(pp); mvneta_mdio_remove(pp); - free_irq(dev->irq, pp); + unregister_cpu_notifier(&pp->cpu_notifier); + for_each_present_cpu(cpu) + smp_call_function_single(cpu, mvneta_percpu_disable, pp, true); + free_percpu_irq(dev->irq, pp->ports); mvneta_cleanup_rxqs(pp); mvneta_cleanup_txqs(pp); @@ -3030,14 +3139,7 @@ static int mvneta_probe(struct platform_device *pdev) const char *managed; int phy_mode; int err; - - /* Our multiqueue support is not complete, so for now, only - * allow the usage of the first RX queue - */ - if (rxq_def != 0) { - dev_err(&pdev->dev, "Invalid rxq_def argument: %d\n", rxq_def); - return -EINVAL; - } + int cpu; dev = alloc_etherdev_mqs(sizeof(struct mvneta_port), txq_number, rxq_number); if (!dev) @@ -3089,6 +3191,7 @@ static int mvneta_probe(struct platform_device *pdev) err = of_property_read_string(dn, "managed", &managed); pp->use_inband_status = (err == 0 && strcmp(managed, "in-band-status") == 0); + pp->cpu_notifier.notifier_call = mvneta_percpu_notifier; pp->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(pp->clk)) { @@ -3105,11 +3208,18 @@ static int mvneta_probe(struct platform_device *pdev) goto err_clk; } + /* Alloc per-cpu port structure */ + pp->ports = alloc_percpu(struct mvneta_pcpu_port); + if (!pp->ports) { + err = -ENOMEM; + goto err_clk; + } + /* Alloc per-cpu stats */ pp->stats = netdev_alloc_pcpu_stats(struct mvneta_pcpu_stats); if (!pp->stats) { err = -ENOMEM; - goto err_clk; + goto err_free_ports; } dt_mac_addr = of_get_mac_address(dn); @@ -3150,7 +3260,12 @@ static int mvneta_probe(struct platform_device *pdev) if (dram_target_info) mvneta_conf_mbus_windows(pp, dram_target_info); - netif_napi_add(dev, &pp->napi, mvneta_poll, NAPI_POLL_WEIGHT); + for_each_present_cpu(cpu) { + struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); + + netif_napi_add(dev, &port->napi, mvneta_poll, NAPI_POLL_WEIGHT); + port->pp = pp; + } dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; dev->hw_features |= dev->features; @@ -3173,12 +3288,16 @@ static int mvneta_probe(struct platform_device *pdev) struct phy_device *phy = of_phy_find_device(dn); mvneta_fixed_link_update(pp, phy); + + put_device(&phy->dev); } return 0; err_free_stats: free_percpu(pp->stats); +err_free_ports: + free_percpu(pp->ports); err_clk: clk_disable_unprepare(pp->clk); err_put_phy_node: @@ -3198,6 +3317,7 @@ static int mvneta_remove(struct platform_device *pdev) unregister_netdev(dev); clk_disable_unprepare(pp->clk); + free_percpu(pp->ports); free_percpu(pp->stats); irq_dispose_mapping(dev->irq); of_node_put(pp->phy_node); diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index d9f4498832a1..5606a043063e 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -4819,6 +4819,18 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port, memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN); + /* if the address is invalid, use a random value */ + if (!is_valid_ether_addr(dev->dev_addr)) { + struct sockaddr sa = { AF_UNSPEC }; + + netdev_warn(dev, + "Invalid MAC address, defaulting to random\n"); + eth_hw_addr_random(dev); + memcpy(sa.sa_data, dev->dev_addr, ETH_ALEN); + if (sky2_set_mac_address(dev, &sa)) + netdev_warn(dev, "Failed to set MAC address.\n"); + } + return dev; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 4726122ea76b..597d8923c8e1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -573,10 +573,8 @@ static int mlx4_en_get_qp(struct mlx4_en_priv *priv) { struct mlx4_en_dev *mdev = priv->mdev; struct mlx4_dev *dev = mdev->dev; - struct mlx4_mac_entry *entry; int index = 0; int err = 0; - u64 reg_id = 0; int *qpn = &priv->base_qpn; u64 mac = mlx4_mac_to_u64(priv->dev->dev_addr); @@ -600,44 +598,11 @@ static int mlx4_en_get_qp(struct mlx4_en_priv *priv) en_dbg(DRV, priv, "Reserved qp %d\n", *qpn); if (err) { en_err(priv, "Failed to reserve qp for mac registration\n"); - goto qp_err; - } - - err = mlx4_en_uc_steer_add(priv, priv->dev->dev_addr, qpn, ®_id); - if (err) - goto steer_err; - - err = mlx4_en_tunnel_steer_add(priv, priv->dev->dev_addr, *qpn, - &priv->tunnel_reg_id); - if (err) - goto tunnel_err; - - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - err = -ENOMEM; - goto alloc_err; + mlx4_unregister_mac(dev, priv->port, mac); + return err; } - memcpy(entry->mac, priv->dev->dev_addr, sizeof(entry->mac)); - memcpy(priv->current_mac, entry->mac, sizeof(priv->current_mac)); - entry->reg_id = reg_id; - - hlist_add_head_rcu(&entry->hlist, - &priv->mac_hash[entry->mac[MLX4_EN_MAC_HASH_IDX]]); return 0; - -alloc_err: - if (priv->tunnel_reg_id) - mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); -tunnel_err: - mlx4_en_uc_steer_release(priv, priv->dev->dev_addr, *qpn, reg_id); - -steer_err: - mlx4_qp_release_range(dev, *qpn, 1); - -qp_err: - mlx4_unregister_mac(dev, priv->port, mac); - return err; } static void mlx4_en_put_qp(struct mlx4_en_priv *priv) @@ -645,39 +610,13 @@ static void mlx4_en_put_qp(struct mlx4_en_priv *priv) struct mlx4_en_dev *mdev = priv->mdev; struct mlx4_dev *dev = mdev->dev; int qpn = priv->base_qpn; - u64 mac; if (dev->caps.steering_mode == MLX4_STEERING_MODE_A0) { - mac = mlx4_mac_to_u64(priv->dev->dev_addr); + u64 mac = mlx4_mac_to_u64(priv->dev->dev_addr); en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n", priv->dev->dev_addr); mlx4_unregister_mac(dev, priv->port, mac); } else { - struct mlx4_mac_entry *entry; - struct hlist_node *tmp; - struct hlist_head *bucket; - unsigned int i; - - for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) { - bucket = &priv->mac_hash[i]; - hlist_for_each_entry_safe(entry, tmp, bucket, hlist) { - mac = mlx4_mac_to_u64(entry->mac); - en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n", - entry->mac); - mlx4_en_uc_steer_release(priv, entry->mac, - qpn, entry->reg_id); - - mlx4_unregister_mac(dev, priv->port, mac); - hlist_del_rcu(&entry->hlist); - kfree_rcu(entry, rcu); - } - } - - if (priv->tunnel_reg_id) { - mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); - priv->tunnel_reg_id = 0; - } - en_dbg(DRV, priv, "Releasing qp: port %d, qpn %d\n", priv->port, qpn); mlx4_qp_release_range(dev, qpn, 1); @@ -1283,6 +1222,75 @@ static void mlx4_en_netpoll(struct net_device *dev) } #endif +static int mlx4_en_set_rss_steer_rules(struct mlx4_en_priv *priv) +{ + u64 reg_id; + int err = 0; + int *qpn = &priv->base_qpn; + struct mlx4_mac_entry *entry; + + err = mlx4_en_uc_steer_add(priv, priv->dev->dev_addr, qpn, ®_id); + if (err) + return err; + + err = mlx4_en_tunnel_steer_add(priv, priv->dev->dev_addr, *qpn, + &priv->tunnel_reg_id); + if (err) + goto tunnel_err; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + err = -ENOMEM; + goto alloc_err; + } + + memcpy(entry->mac, priv->dev->dev_addr, sizeof(entry->mac)); + memcpy(priv->current_mac, entry->mac, sizeof(priv->current_mac)); + entry->reg_id = reg_id; + hlist_add_head_rcu(&entry->hlist, + &priv->mac_hash[entry->mac[MLX4_EN_MAC_HASH_IDX]]); + + return 0; + +alloc_err: + if (priv->tunnel_reg_id) + mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); + +tunnel_err: + mlx4_en_uc_steer_release(priv, priv->dev->dev_addr, *qpn, reg_id); + return err; +} + +static void mlx4_en_delete_rss_steer_rules(struct mlx4_en_priv *priv) +{ + u64 mac; + unsigned int i; + int qpn = priv->base_qpn; + struct hlist_head *bucket; + struct hlist_node *tmp; + struct mlx4_mac_entry *entry; + + for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) { + bucket = &priv->mac_hash[i]; + hlist_for_each_entry_safe(entry, tmp, bucket, hlist) { + mac = mlx4_mac_to_u64(entry->mac); + en_dbg(DRV, priv, "Registering MAC:%pM for deleting\n", + entry->mac); + mlx4_en_uc_steer_release(priv, entry->mac, + qpn, entry->reg_id); + + mlx4_unregister_mac(priv->mdev->dev, priv->port, mac); + hlist_del_rcu(&entry->hlist); + kfree_rcu(entry, rcu); + } + } + + if (priv->tunnel_reg_id) { + mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); + priv->tunnel_reg_id = 0; + } +} + static void mlx4_en_tx_timeout(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); @@ -1684,6 +1692,11 @@ int mlx4_en_start_port(struct net_device *dev) goto tx_err; } + /* Set Unicast and VXLAN steering rules */ + if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0 && + mlx4_en_set_rss_steer_rules(priv)) + mlx4_warn(mdev, "Failed setting steering rules\n"); + /* Attach rx QP to bradcast address */ eth_broadcast_addr(&mc_list[10]); mc_list[5] = priv->port; /* needed for B0 steering support */ @@ -1831,6 +1844,9 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) for (i = 0; i < priv->tx_ring_num; i++) mlx4_en_free_tx_buf(dev, priv->tx_ring[i]); + if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0) + mlx4_en_delete_rss_steer_rules(priv); + /* Free RSS qps */ mlx4_en_release_rss_steer(priv); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 4402a1e48c9b..e7a5000aa12c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -1047,13 +1047,15 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) /* If we used up all the quota - we're probably not done yet... */ if (done == budget) { - int cpu_curr; const struct cpumask *aff; + struct irq_data *idata; + int cpu_curr; INC_PERF_COUNTER(priv->pstats.napi_quota); cpu_curr = smp_processor_id(); - aff = irq_desc_get_irq_data(cq->irq_desc)->affinity; + idata = irq_desc_get_irq_data(cq->irq_desc); + aff = irq_data_get_affinity_mask(idata); if (likely(cpumask_test_cpu(cpu_curr, aff))) return budget; @@ -1268,8 +1270,6 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) rss_context->hash_fn = MLX4_RSS_HASH_TOP; memcpy(rss_context->rss_key, priv->rss_key, MLX4_EN_RSS_KEY_SIZE); - netdev_rss_key_fill(rss_context->rss_key, - MLX4_EN_RSS_KEY_SIZE); } else { en_err(priv, "Unknown RSS hash function requested\n"); err = -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index bd9ea0d01aae..1d4e2e054647 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -1184,10 +1184,11 @@ out: if (prot == MLX4_PROT_ETH) { /* manage the steering entry for promisc mode */ if (new_entry) - new_steering_entry(dev, port, steer, index, qp->qpn); + err = new_steering_entry(dev, port, steer, + index, qp->qpn); else - existing_steering_entry(dev, port, steer, - index, qp->qpn); + err = existing_steering_entry(dev, port, steer, + index, qp->qpn); } if (err && link && index != -1) { if (index < dev->caps.num_mgms) diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 20268634a9ab..3311f35d08e0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -422,15 +422,15 @@ int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn, u64 qp_mask = 0; int err = 0; + if (!attr || (attr & ~MLX4_UPDATE_QP_SUPPORTED_ATTRS)) + return -EINVAL; + mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) return PTR_ERR(mailbox); cmd = (struct mlx4_update_qp_context *)mailbox->buf; - if (!attr || (attr & ~MLX4_UPDATE_QP_SUPPORTED_ATTRS)) - return -EINVAL; - if (attr & MLX4_UPDATE_QP_SMAC) { pri_addr_path_mask |= 1ULL << MLX4_UPD_QP_PATH_MASK_MAC_INDEX; cmd->qp_context.pri_path.grh_mylmc = params->smac_index; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 731423ca575d..ac4b99ab1f85 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -1238,8 +1238,10 @@ static int add_res_range(struct mlx4_dev *dev, int slave, u64 base, int count, return 0; undo: - for (--i; i >= base; --i) + for (--i; i >= 0; --i) { rb_erase(&res_arr[i]->node, root); + list_del_init(&res_arr[i]->list); + } spin_unlock_irq(mlx4_tlock(dev)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 75ff58dc1ff5..c3e54b7e8780 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -254,6 +254,10 @@ static void dump_buf(void *buf, int size, int data_only, int offset) pr_debug("\n"); } +enum { + MLX5_DRIVER_STATUS_ABORTED = 0xfe, +}; + const char *mlx5_command_str(int command) { switch (command) { @@ -473,6 +477,7 @@ static void cmd_work_handler(struct work_struct *work) struct mlx5_core_dev *dev = container_of(cmd, struct mlx5_core_dev, cmd); struct mlx5_cmd_layout *lay; struct semaphore *sem; + unsigned long flags; sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem; down(sem); @@ -485,6 +490,9 @@ static void cmd_work_handler(struct work_struct *work) } } else { ent->idx = cmd->max_reg_cmds; + spin_lock_irqsave(&cmd->alloc_lock, flags); + clear_bit(ent->idx, &cmd->bitmask); + spin_unlock_irqrestore(&cmd->alloc_lock, flags); } ent->token = alloc_token(cmd); @@ -1081,7 +1089,7 @@ static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg) } } -void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) +void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec) { struct mlx5_cmd *cmd = &dev->cmd; struct mlx5_cmd_work_ent *ent; @@ -1092,7 +1100,10 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) s64 ds; struct mlx5_cmd_stats *stats; unsigned long flags; + unsigned long vector; + /* there can be at most 32 command queues */ + vector = vec & 0xffffffff; for (i = 0; i < (1 << cmd->log_sz); i++) { if (test_bit(i, &vector)) { struct semaphore *sem; @@ -1110,11 +1121,16 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) ent->ret = verify_signature(ent); else ent->ret = 0; - ent->status = ent->lay->status_own >> 1; + if (vec & MLX5_TRIGGERED_CMD_COMP) + ent->status = MLX5_DRIVER_STATUS_ABORTED; + else + ent->status = ent->lay->status_own >> 1; + mlx5_core_dbg(dev, "command completed. ret 0x%x, delivery status %s(0x%x)\n", ent->ret, deliv_status_to_str(ent->status), ent->status); } free_ent(cmd, ent->idx); + if (ent->callback) { ds = ent->ts2 - ent->ts1; if (ent->op < ARRAY_SIZE(cmd->stats)) { @@ -1136,6 +1152,7 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) mlx5_free_cmd_msg(dev, ent->out); free_msg(dev, ent->in); + err = err ? err : ent->status; free_cmd(ent); callback(err, context); } else { @@ -1363,6 +1380,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) int err; int i; + memset(cmd, 0, sizeof(*cmd)); cmd_if_rev = cmdif_rev(dev); if (cmd_if_rev != CMD_IF_REV) { dev_err(&dev->pdev->dev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c index 04ab7e445eae..b51e42d6fbec 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c @@ -242,6 +242,7 @@ int mlx5_init_cq_table(struct mlx5_core_dev *dev) struct mlx5_cq_table *table = &dev->priv.cq_table; int err; + memset(table, 0, sizeof(*table)); spin_lock_init(&table->lock); INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); err = mlx5_cq_debugfs_init(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 59874d666cff..bb801a9bbd42 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1367,13 +1367,13 @@ int mlx5e_open_locked(struct net_device *netdev) err = mlx5e_set_dev_port_mtu(netdev); if (err) - return err; + goto err_clear_state_opened_flag; err = mlx5e_open_channels(priv); if (err) { netdev_err(netdev, "%s: mlx5e_open_channels failed, %d\n", __func__, err); - return err; + goto err_clear_state_opened_flag; } mlx5e_update_carrier(priv); @@ -1382,6 +1382,10 @@ int mlx5e_open_locked(struct net_device *netdev) schedule_delayed_work(&priv->update_stats_work, 0); return 0; + +err_clear_state_opened_flag: + clear_bit(MLX5E_STATE_OPENED, &priv->state); + return err; } static int mlx5e_open(struct net_device *netdev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index a40b96d4c662..1f01fe8fde42 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -346,6 +346,7 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx, int inlen; eq->nent = roundup_pow_of_two(nent + MLX5_NUM_SPARE_EQE); + eq->cons_index = 0; err = mlx5_buf_alloc(dev, eq->nent * MLX5_EQE_SIZE, &eq->buf); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index aa0d5ffe92d8..9335e5ae18cc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -200,25 +200,3 @@ int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev) return err; } - -int mlx5_core_query_special_context(struct mlx5_core_dev *dev, u32 *rsvd_lkey) -{ - struct mlx5_cmd_query_special_contexts_mbox_in in; - struct mlx5_cmd_query_special_contexts_mbox_out out; - int err; - - memset(&in, 0, sizeof(in)); - memset(&out, 0, sizeof(out)); - in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS); - err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); - if (err) - return err; - - if (out.hdr.status) - err = mlx5_cmd_status_to_err(&out.hdr); - - *rsvd_lkey = be32_to_cpu(out.resd_lkey); - - return err; -} -EXPORT_SYMBOL(mlx5_core_query_special_context); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 292d76f2a904..9b81e1ceb8de 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -46,39 +46,27 @@ enum { enum { MLX5_HEALTH_SYNDR_FW_ERR = 0x1, MLX5_HEALTH_SYNDR_IRISC_ERR = 0x7, + MLX5_HEALTH_SYNDR_HW_UNRECOVERABLE_ERR = 0x8, MLX5_HEALTH_SYNDR_CRC_ERR = 0x9, MLX5_HEALTH_SYNDR_FETCH_PCI_ERR = 0xa, MLX5_HEALTH_SYNDR_HW_FTL_ERR = 0xb, MLX5_HEALTH_SYNDR_ASYNC_EQ_OVERRUN_ERR = 0xc, MLX5_HEALTH_SYNDR_EQ_ERR = 0xd, + MLX5_HEALTH_SYNDR_EQ_INV = 0xe, MLX5_HEALTH_SYNDR_FFSER_ERR = 0xf, + MLX5_HEALTH_SYNDR_HIGH_TEMP = 0x10 }; -static DEFINE_SPINLOCK(health_lock); -static LIST_HEAD(health_list); -static struct work_struct health_work; - static void health_care(struct work_struct *work) { - struct mlx5_core_health *health, *n; + struct mlx5_core_health *health; struct mlx5_core_dev *dev; struct mlx5_priv *priv; - LIST_HEAD(tlist); - - spin_lock_irq(&health_lock); - list_splice_init(&health_list, &tlist); - spin_unlock_irq(&health_lock); - - list_for_each_entry_safe(health, n, &tlist, list) { - priv = container_of(health, struct mlx5_priv, health); - dev = container_of(priv, struct mlx5_core_dev, priv); - mlx5_core_warn(dev, "handling bad device here\n"); - /* nothing yet */ - spin_lock_irq(&health_lock); - list_del_init(&health->list); - spin_unlock_irq(&health_lock); - } + health = container_of(work, struct mlx5_core_health, work); + priv = container_of(health, struct mlx5_priv, health); + dev = container_of(priv, struct mlx5_core_dev, priv); + mlx5_core_warn(dev, "handling bad device here\n"); } static const char *hsynd_str(u8 synd) @@ -88,6 +76,8 @@ static const char *hsynd_str(u8 synd) return "firmware internal error"; case MLX5_HEALTH_SYNDR_IRISC_ERR: return "irisc not responding"; + case MLX5_HEALTH_SYNDR_HW_UNRECOVERABLE_ERR: + return "unrecoverable hardware error"; case MLX5_HEALTH_SYNDR_CRC_ERR: return "firmware CRC error"; case MLX5_HEALTH_SYNDR_FETCH_PCI_ERR: @@ -98,39 +88,52 @@ static const char *hsynd_str(u8 synd) return "async EQ buffer overrun"; case MLX5_HEALTH_SYNDR_EQ_ERR: return "EQ error"; + case MLX5_HEALTH_SYNDR_EQ_INV: + return "Invalid EQ refrenced"; case MLX5_HEALTH_SYNDR_FFSER_ERR: return "FFSER error"; + case MLX5_HEALTH_SYNDR_HIGH_TEMP: + return "High temprature"; default: return "unrecognized error"; } } -static u16 read_be16(__be16 __iomem *p) +static u16 get_maj(u32 fw) { - return swab16(readl((__force u16 __iomem *) p)); + return fw >> 28; } -static u32 read_be32(__be32 __iomem *p) +static u16 get_min(u32 fw) { - return swab32(readl((__force u32 __iomem *) p)); + return fw >> 16 & 0xfff; +} + +static u16 get_sub(u32 fw) +{ + return fw & 0xffff; } static void print_health_info(struct mlx5_core_dev *dev) { struct mlx5_core_health *health = &dev->priv.health; struct health_buffer __iomem *h = health->health; + char fw_str[18]; + u32 fw; int i; for (i = 0; i < ARRAY_SIZE(h->assert_var); i++) - pr_info("assert_var[%d] 0x%08x\n", i, read_be32(h->assert_var + i)); - - pr_info("assert_exit_ptr 0x%08x\n", read_be32(&h->assert_exit_ptr)); - pr_info("assert_callra 0x%08x\n", read_be32(&h->assert_callra)); - pr_info("fw_ver 0x%08x\n", read_be32(&h->fw_ver)); - pr_info("hw_id 0x%08x\n", read_be32(&h->hw_id)); - pr_info("irisc_index %d\n", readb(&h->irisc_index)); - pr_info("synd 0x%x: %s\n", readb(&h->synd), hsynd_str(readb(&h->synd))); - pr_info("ext_sync 0x%04x\n", read_be16(&h->ext_sync)); + dev_err(&dev->pdev->dev, "assert_var[%d] 0x%08x\n", i, ioread32be(h->assert_var + i)); + + dev_err(&dev->pdev->dev, "assert_exit_ptr 0x%08x\n", ioread32be(&h->assert_exit_ptr)); + dev_err(&dev->pdev->dev, "assert_callra 0x%08x\n", ioread32be(&h->assert_callra)); + fw = ioread32be(&h->fw_ver); + sprintf(fw_str, "%d.%d.%d", get_maj(fw), get_min(fw), get_sub(fw)); + dev_err(&dev->pdev->dev, "fw_ver %s\n", fw_str); + dev_err(&dev->pdev->dev, "hw_id 0x%08x\n", ioread32be(&h->hw_id)); + dev_err(&dev->pdev->dev, "irisc_index %d\n", ioread8(&h->irisc_index)); + dev_err(&dev->pdev->dev, "synd 0x%x: %s\n", ioread8(&h->synd), hsynd_str(ioread8(&h->synd))); + dev_err(&dev->pdev->dev, "ext_synd 0x%04x\n", ioread16be(&h->ext_synd)); } static void poll_health(unsigned long data) @@ -150,11 +153,7 @@ static void poll_health(unsigned long data) if (health->miss_counter == MAX_MISSES) { mlx5_core_err(dev, "device's health compromised\n"); print_health_info(dev); - spin_lock_irq(&health_lock); - list_add_tail(&health->list, &health_list); - spin_unlock_irq(&health_lock); - - queue_work(mlx5_core_wq, &health_work); + queue_work(health->wq, &health->work); } else { get_random_bytes(&next, sizeof(next)); next %= HZ; @@ -167,7 +166,6 @@ void mlx5_start_health_poll(struct mlx5_core_dev *dev) { struct mlx5_core_health *health = &dev->priv.health; - INIT_LIST_HEAD(&health->list); init_timer(&health->timer); health->health = &dev->iseg->health; health->health_counter = &dev->iseg->health_counter; @@ -183,18 +181,33 @@ void mlx5_stop_health_poll(struct mlx5_core_dev *dev) struct mlx5_core_health *health = &dev->priv.health; del_timer_sync(&health->timer); - - spin_lock_irq(&health_lock); - if (!list_empty(&health->list)) - list_del_init(&health->list); - spin_unlock_irq(&health_lock); } -void mlx5_health_cleanup(void) +void mlx5_health_cleanup(struct mlx5_core_dev *dev) { + struct mlx5_core_health *health = &dev->priv.health; + + destroy_workqueue(health->wq); } -void __init mlx5_health_init(void) +int mlx5_health_init(struct mlx5_core_dev *dev) { - INIT_WORK(&health_work, health_care); + struct mlx5_core_health *health; + char *name; + + health = &dev->priv.health; + name = kmalloc(64, GFP_KERNEL); + if (!name) + return -ENOMEM; + + strcpy(name, "mlx5_health"); + strcat(name, dev_name(&dev->pdev->dev)); + health->wq = create_singlethread_workqueue(name); + kfree(name); + if (!health->wq) + return -ENOMEM; + + INIT_WORK(&health->work, health_care); + + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 03aabdd79abe..b6edc58766ad 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -62,7 +62,6 @@ static int prof_sel = MLX5_DEFAULT_PROF; module_param_named(prof_sel, prof_sel, int, 0444); MODULE_PARM_DESC(prof_sel, "profile selector. Valid range 0 - 2"); -struct workqueue_struct *mlx5_core_wq; static LIST_HEAD(intf_list); static LIST_HEAD(dev_list); static DEFINE_MUTEX(intf_mutex); @@ -672,12 +671,126 @@ static void unmap_bf_area(struct mlx5_core_dev *dev) io_mapping_free(dev->priv.bf_mapping); } -static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) +static void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) +{ + struct mlx5_device_context *dev_ctx; + struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); + + dev_ctx = kmalloc(sizeof(*dev_ctx), GFP_KERNEL); + if (!dev_ctx) + return; + + dev_ctx->intf = intf; + dev_ctx->context = intf->add(dev); + + if (dev_ctx->context) { + spin_lock_irq(&priv->ctx_lock); + list_add_tail(&dev_ctx->list, &priv->ctx_list); + spin_unlock_irq(&priv->ctx_lock); + } else { + kfree(dev_ctx); + } +} + +static void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv) +{ + struct mlx5_device_context *dev_ctx; + struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); + + list_for_each_entry(dev_ctx, &priv->ctx_list, list) + if (dev_ctx->intf == intf) { + spin_lock_irq(&priv->ctx_lock); + list_del(&dev_ctx->list); + spin_unlock_irq(&priv->ctx_lock); + + intf->remove(dev, dev_ctx->context); + kfree(dev_ctx); + return; + } +} + +static int mlx5_register_device(struct mlx5_core_dev *dev) { struct mlx5_priv *priv = &dev->priv; - int err; + struct mlx5_interface *intf; + + mutex_lock(&intf_mutex); + list_add_tail(&priv->dev_list, &dev_list); + list_for_each_entry(intf, &intf_list, list) + mlx5_add_device(intf, priv); + mutex_unlock(&intf_mutex); + + return 0; +} + +static void mlx5_unregister_device(struct mlx5_core_dev *dev) +{ + struct mlx5_priv *priv = &dev->priv; + struct mlx5_interface *intf; + + mutex_lock(&intf_mutex); + list_for_each_entry(intf, &intf_list, list) + mlx5_remove_device(intf, priv); + list_del(&priv->dev_list); + mutex_unlock(&intf_mutex); +} + +int mlx5_register_interface(struct mlx5_interface *intf) +{ + struct mlx5_priv *priv; + + if (!intf->add || !intf->remove) + return -EINVAL; + + mutex_lock(&intf_mutex); + list_add_tail(&intf->list, &intf_list); + list_for_each_entry(priv, &dev_list, dev_list) + mlx5_add_device(intf, priv); + mutex_unlock(&intf_mutex); + + return 0; +} +EXPORT_SYMBOL(mlx5_register_interface); + +void mlx5_unregister_interface(struct mlx5_interface *intf) +{ + struct mlx5_priv *priv; + + mutex_lock(&intf_mutex); + list_for_each_entry(priv, &dev_list, dev_list) + mlx5_remove_device(intf, priv); + list_del(&intf->list); + mutex_unlock(&intf_mutex); +} +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 int mlx5_pci_init(struct mlx5_core_dev *dev, struct mlx5_priv *priv) +{ + struct pci_dev *pdev = dev->pdev; + int err = 0; - dev->pdev = pdev; pci_set_drvdata(dev->pdev, dev); strncpy(priv->name, dev_name(&pdev->dev), MLX5_MAX_NAME_LEN); priv->name[MLX5_MAX_NAME_LEN - 1] = 0; @@ -721,13 +834,42 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) dev_err(&pdev->dev, "Failed mapping initialization segment, aborting\n"); goto err_clr_master; } + + return 0; + +err_clr_master: + pci_clear_master(dev->pdev); + release_bar(dev->pdev); +err_disable: + pci_disable_device(dev->pdev); + +err_dbg: + debugfs_remove(priv->dbg_root); + return err; +} + +static void mlx5_pci_close(struct mlx5_core_dev *dev, struct mlx5_priv *priv) +{ + iounmap(dev->iseg); + pci_clear_master(dev->pdev); + release_bar(dev->pdev); + pci_disable_device(dev->pdev); + debugfs_remove(priv->dbg_root); +} + +#define MLX5_IB_MOD "mlx5_ib" +static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) +{ + struct pci_dev *pdev = dev->pdev; + int err; + dev_info(&pdev->dev, "firmware version: %d.%d.%d\n", fw_rev_maj(dev), fw_rev_min(dev), fw_rev_sub(dev)); err = mlx5_cmd_init(dev); if (err) { dev_err(&pdev->dev, "Failed initializing command interface, aborting\n"); - goto err_unmap; + return err; } mlx5_pagealloc_init(dev); @@ -842,8 +984,25 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) mlx5_init_srq_table(dev); mlx5_init_mr_table(dev); + err = mlx5_register_device(dev); + if (err) { + dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err); + goto err_reg_dev; + } + + err = request_module_nowait(MLX5_IB_MOD); + if (err) + pr_info("failed request module on %s\n", MLX5_IB_MOD); + return 0; +err_reg_dev: + mlx5_cleanup_mr_table(dev); + mlx5_cleanup_srq_table(dev); + mlx5_cleanup_qp_table(dev); + mlx5_cleanup_cq_table(dev); + mlx5_irq_clear_affinity_hints(dev); + err_unmap_bf_area: unmap_bf_area(dev); @@ -881,25 +1040,15 @@ err_pagealloc_cleanup: mlx5_pagealloc_cleanup(dev); mlx5_cmd_cleanup(dev); -err_unmap: - iounmap(dev->iseg); - -err_clr_master: - pci_clear_master(dev->pdev); - release_bar(dev->pdev); - -err_disable: - pci_disable_device(dev->pdev); - -err_dbg: - debugfs_remove(priv->dbg_root); return err; } -static void mlx5_dev_cleanup(struct mlx5_core_dev *dev) +static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) { - struct mlx5_priv *priv = &dev->priv; + int err; + mlx5_unregister_device(dev); + mlx5_cleanup_mr_table(dev); mlx5_cleanup_srq_table(dev); mlx5_cleanup_qp_table(dev); mlx5_cleanup_cq_table(dev); @@ -911,139 +1060,23 @@ static void mlx5_dev_cleanup(struct mlx5_core_dev *dev) mlx5_eq_cleanup(dev); mlx5_disable_msix(dev); mlx5_stop_health_poll(dev); - if (mlx5_cmd_teardown_hca(dev)) { + err = mlx5_cmd_teardown_hca(dev); + if (err) { dev_err(&dev->pdev->dev, "tear_down_hca failed, skip cleanup\n"); - return; + goto out; } mlx5_pagealloc_stop(dev); mlx5_reclaim_startup_pages(dev); mlx5_core_disable_hca(dev); mlx5_pagealloc_cleanup(dev); mlx5_cmd_cleanup(dev); - iounmap(dev->iseg); - pci_clear_master(dev->pdev); - release_bar(dev->pdev); - pci_disable_device(dev->pdev); - debugfs_remove(priv->dbg_root); -} - -static void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) -{ - struct mlx5_device_context *dev_ctx; - struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); - - dev_ctx = kmalloc(sizeof(*dev_ctx), GFP_KERNEL); - if (!dev_ctx) { - pr_warn("mlx5_add_device: alloc context failed\n"); - return; - } - - dev_ctx->intf = intf; - dev_ctx->context = intf->add(dev); - - if (dev_ctx->context) { - spin_lock_irq(&priv->ctx_lock); - list_add_tail(&dev_ctx->list, &priv->ctx_list); - spin_unlock_irq(&priv->ctx_lock); - } else { - kfree(dev_ctx); - } -} - -static void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv) -{ - struct mlx5_device_context *dev_ctx; - struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); - - list_for_each_entry(dev_ctx, &priv->ctx_list, list) - if (dev_ctx->intf == intf) { - spin_lock_irq(&priv->ctx_lock); - list_del(&dev_ctx->list); - spin_unlock_irq(&priv->ctx_lock); - - intf->remove(dev, dev_ctx->context); - kfree(dev_ctx); - return; - } -} -static int mlx5_register_device(struct mlx5_core_dev *dev) -{ - struct mlx5_priv *priv = &dev->priv; - struct mlx5_interface *intf; - - mutex_lock(&intf_mutex); - list_add_tail(&priv->dev_list, &dev_list); - list_for_each_entry(intf, &intf_list, list) - mlx5_add_device(intf, priv); - mutex_unlock(&intf_mutex); - - return 0; -} -static void mlx5_unregister_device(struct mlx5_core_dev *dev) -{ - struct mlx5_priv *priv = &dev->priv; - struct mlx5_interface *intf; - mutex_lock(&intf_mutex); - list_for_each_entry(intf, &intf_list, list) - mlx5_remove_device(intf, priv); - list_del(&priv->dev_list); - mutex_unlock(&intf_mutex); -} - -int mlx5_register_interface(struct mlx5_interface *intf) -{ - struct mlx5_priv *priv; - - if (!intf->add || !intf->remove) - return -EINVAL; - - mutex_lock(&intf_mutex); - list_add_tail(&intf->list, &intf_list); - list_for_each_entry(priv, &dev_list, dev_list) - mlx5_add_device(intf, priv); - mutex_unlock(&intf_mutex); - - return 0; -} -EXPORT_SYMBOL(mlx5_register_interface); - -void mlx5_unregister_interface(struct mlx5_interface *intf) -{ - struct mlx5_priv *priv; - - mutex_lock(&intf_mutex); - list_for_each_entry(priv, &dev_list, dev_list) - mlx5_remove_device(intf, priv); - list_del(&intf->list); - mutex_unlock(&intf_mutex); -} -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; +out: + return err; } -EXPORT_SYMBOL(mlx5_get_protocol_dev); static void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, - unsigned long param) + unsigned long param) { struct mlx5_priv *priv = &dev->priv; struct mlx5_device_context *dev_ctx; @@ -1064,7 +1097,6 @@ struct mlx5_core_event_handler { void *data); }; -#define MLX5_IB_MOD "mlx5_ib" static int init_one(struct pci_dev *pdev, const struct pci_device_id *id) @@ -1088,40 +1120,55 @@ static int init_one(struct pci_dev *pdev, prof_sel = MLX5_DEFAULT_PROF; } dev->profile = &profile[prof_sel]; + dev->pdev = pdev; dev->event = mlx5_core_event; INIT_LIST_HEAD(&priv->ctx_list); spin_lock_init(&priv->ctx_lock); - err = mlx5_dev_init(dev, pdev); + err = mlx5_pci_init(dev, priv); if (err) { - dev_err(&pdev->dev, "mlx5_dev_init failed %d\n", err); - goto out; + dev_err(&pdev->dev, "mlx5_pci_init failed with error code %d\n", err); + goto clean_dev; } - err = mlx5_register_device(dev); + err = mlx5_health_init(dev); if (err) { - dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err); - goto out_init; + dev_err(&pdev->dev, "mlx5_health_init failed with error code %d\n", err); + goto close_pci; } - err = request_module_nowait(MLX5_IB_MOD); - if (err) - pr_info("failed request module on %s\n", MLX5_IB_MOD); + err = mlx5_load_one(dev, priv); + if (err) { + dev_err(&pdev->dev, "mlx5_load_one failed with error code %d\n", err); + goto clean_health; + } return 0; -out_init: - mlx5_dev_cleanup(dev); -out: +clean_health: + mlx5_health_cleanup(dev); +close_pci: + mlx5_pci_close(dev, priv); +clean_dev: + pci_set_drvdata(pdev, NULL); kfree(dev); + return err; } + static void remove_one(struct pci_dev *pdev) { struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_priv *priv = &dev->priv; - mlx5_unregister_device(dev); - mlx5_dev_cleanup(dev); + if (mlx5_unload_one(dev, priv)) { + dev_err(&dev->pdev->dev, "mlx5_unload_one failed\n"); + mlx5_health_cleanup(dev); + return; + } + mlx5_health_cleanup(dev); + mlx5_pci_close(dev, priv); + pci_set_drvdata(pdev, NULL); kfree(dev); } @@ -1149,16 +1196,10 @@ static int __init init(void) int err; mlx5_register_debugfs(); - mlx5_core_wq = create_singlethread_workqueue("mlx5_core_wq"); - if (!mlx5_core_wq) { - err = -ENOMEM; - goto err_debug; - } - mlx5_health_init(); err = pci_register_driver(&mlx5_core_driver); if (err) - goto err_health; + goto err_debug; #ifdef CONFIG_MLX5_CORE_EN mlx5e_init(); @@ -1166,9 +1207,6 @@ static int __init init(void) return 0; -err_health: - mlx5_health_cleanup(); - destroy_workqueue(mlx5_core_wq); err_debug: mlx5_unregister_debugfs(); return err; @@ -1180,8 +1218,6 @@ static void __exit cleanup(void) mlx5e_cleanup(); #endif pci_unregister_driver(&mlx5_core_driver); - mlx5_health_cleanup(); - destroy_workqueue(mlx5_core_wq); mlx5_unregister_debugfs(); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 566a70488db1..30c0be721b08 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -43,25 +43,25 @@ extern int mlx5_core_debug_mask; -#define mlx5_core_dbg(dev, format, ...) \ - pr_debug("%s:%s:%d:(pid %d): " format, \ - (dev)->priv.name, __func__, __LINE__, current->pid, \ +#define mlx5_core_dbg(__dev, format, ...) \ + dev_dbg(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format, \ + (__dev)->priv.name, __func__, __LINE__, current->pid, \ ##__VA_ARGS__) -#define mlx5_core_dbg_mask(dev, mask, format, ...) \ +#define mlx5_core_dbg_mask(__dev, mask, format, ...) \ do { \ if ((mask) & mlx5_core_debug_mask) \ - mlx5_core_dbg(dev, format, ##__VA_ARGS__); \ + mlx5_core_dbg(__dev, format, ##__VA_ARGS__); \ } while (0) -#define mlx5_core_err(dev, format, ...) \ - pr_err("%s:%s:%d:(pid %d): " format, \ - (dev)->priv.name, __func__, __LINE__, current->pid, \ +#define mlx5_core_err(__dev, format, ...) \ + dev_err(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format, \ + (__dev)->priv.name, __func__, __LINE__, current->pid, \ ##__VA_ARGS__) -#define mlx5_core_warn(dev, format, ...) \ - pr_warn("%s:%s:%d:(pid %d): " format, \ - (dev)->priv.name, __func__, __LINE__, current->pid, \ +#define mlx5_core_warn(__dev, format, ...) \ + dev_warn(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format, \ + (__dev)->priv.name, __func__, __LINE__, current->pid, \ ##__VA_ARGS__) enum { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mr.c b/drivers/net/ethernet/mellanox/mlx5/core/mr.c index 1adb300dd850..6fa22b51e460 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mr.c @@ -40,6 +40,7 @@ void mlx5_init_mr_table(struct mlx5_core_dev *dev) { struct mlx5_mr_table *table = &dev->priv.mr_table; + memset(table, 0, sizeof(*table)); rwlock_init(&table->lock); INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index 8a64542abc16..76432a510ac2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -275,12 +275,36 @@ out_alloc: return err; } + +static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id) +{ + struct mlx5_manage_pages_inbox *in; + struct mlx5_manage_pages_outbox out; + int err; + + in = kzalloc(sizeof(*in), GFP_KERNEL); + if (!in) + return; + + memset(&out, 0, sizeof(out)); + in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES); + in->hdr.opmod = cpu_to_be16(MLX5_PAGES_CANT_GIVE); + in->func_id = cpu_to_be16(func_id); + err = mlx5_cmd_exec(dev, in, sizeof(*in), &out, sizeof(out)); + if (!err) + err = mlx5_cmd_status_to_err(&out.hdr); + + if (err) + mlx5_core_warn(dev, "page notify failed\n"); + + kfree(in); +} + static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, int notify_fail) { struct mlx5_manage_pages_inbox *in; struct mlx5_manage_pages_outbox out; - struct mlx5_manage_pages_inbox *nin; int inlen; u64 addr; int err; @@ -289,8 +313,9 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, inlen = sizeof(*in) + npages * sizeof(in->pas[0]); in = mlx5_vzalloc(inlen); if (!in) { + err = -ENOMEM; mlx5_core_warn(dev, "vzalloc failed %d\n", inlen); - return -ENOMEM; + goto out_free; } memset(&out, 0, sizeof(out)); @@ -316,43 +341,29 @@ retry: if (err) { mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n", func_id, npages, err); - goto out_alloc; + goto out_4k; } dev->priv.fw_pages += npages; - if (out.hdr.status) { - err = mlx5_cmd_status_to_err(&out.hdr); - if (err) { - mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n", - func_id, npages, out.hdr.status); - goto out_alloc; - } + err = mlx5_cmd_status_to_err(&out.hdr); + if (err) { + mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n", + func_id, npages, out.hdr.status); + goto out_4k; } mlx5_core_dbg(dev, "err %d\n", err); - goto out_free; - -out_alloc: - if (notify_fail) { - nin = kzalloc(sizeof(*nin), GFP_KERNEL); - if (!nin) { - mlx5_core_warn(dev, "allocation failed\n"); - goto out_4k; - } - memset(&out, 0, sizeof(out)); - nin->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES); - nin->hdr.opmod = cpu_to_be16(MLX5_PAGES_CANT_GIVE); - if (mlx5_cmd_exec(dev, nin, sizeof(*nin), &out, sizeof(out))) - mlx5_core_warn(dev, "page notify failed\n"); - kfree(nin); - } + kvfree(in); + return 0; out_4k: for (i--; i >= 0; i--) free_4k(dev, be64_to_cpu(in->pas[i])); out_free: kvfree(in); + if (notify_fail) + page_notify_fail(dev, func_id); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index 821caaab9bfb..ae302614e74b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -90,16 +90,13 @@ int mlx5_set_port_caps(struct mlx5_core_dev *dev, u8 port_num, u32 caps) { struct mlx5_reg_pcap in; struct mlx5_reg_pcap out; - int err; memset(&in, 0, sizeof(in)); in.caps_127_96 = cpu_to_be32(caps); in.port_num = port_num; - err = mlx5_core_access_reg(dev, &in, sizeof(in), &out, - sizeof(out), MLX5_REG_PCAP, 0, 1); - - return err; + return mlx5_core_access_reg(dev, &in, sizeof(in), &out, + sizeof(out), MLX5_REG_PCAP, 0, 1); } EXPORT_SYMBOL_GPL(mlx5_set_port_caps); @@ -107,16 +104,13 @@ int mlx5_query_port_ptys(struct mlx5_core_dev *dev, u32 *ptys, int ptys_size, int proto_mask, u8 local_port) { u32 in[MLX5_ST_SZ_DW(ptys_reg)]; - int err; memset(in, 0, sizeof(in)); MLX5_SET(ptys_reg, in, local_port, local_port); MLX5_SET(ptys_reg, in, proto_mask, proto_mask); - err = mlx5_core_access_reg(dev, in, sizeof(in), ptys, - ptys_size, MLX5_REG_PTYS, 0, 0); - - return err; + return mlx5_core_access_reg(dev, in, sizeof(in), ptys, + ptys_size, MLX5_REG_PTYS, 0, 0); } EXPORT_SYMBOL_GPL(mlx5_query_port_ptys); @@ -199,7 +193,6 @@ int mlx5_set_port_proto(struct mlx5_core_dev *dev, u32 proto_admin, { u32 in[MLX5_ST_SZ_DW(ptys_reg)]; u32 out[MLX5_ST_SZ_DW(ptys_reg)]; - int err; memset(in, 0, sizeof(in)); @@ -210,9 +203,8 @@ int mlx5_set_port_proto(struct mlx5_core_dev *dev, u32 proto_admin, else MLX5_SET(ptys_reg, in, ib_proto_admin, proto_admin); - err = mlx5_core_access_reg(dev, in, sizeof(in), out, - sizeof(out), MLX5_REG_PTYS, 0, 1); - return err; + return mlx5_core_access_reg(dev, in, sizeof(in), out, + sizeof(out), MLX5_REG_PTYS, 0, 1); } EXPORT_SYMBOL_GPL(mlx5_set_port_proto); @@ -250,7 +242,7 @@ int mlx5_query_port_admin_status(struct mlx5_core_dev *dev, return err; *status = MLX5_GET(paos_reg, out, admin_status); - return err; + return 0; } EXPORT_SYMBOL_GPL(mlx5_query_port_admin_status); @@ -308,15 +300,12 @@ static int mlx5_query_port_pvlc(struct mlx5_core_dev *dev, u32 *pvlc, int pvlc_size, u8 local_port) { u32 in[MLX5_ST_SZ_DW(pvlc_reg)]; - int err; memset(in, 0, sizeof(in)); MLX5_SET(ptys_reg, in, local_port, local_port); - err = mlx5_core_access_reg(dev, in, sizeof(in), pvlc, - pvlc_size, MLX5_REG_PVLC, 0, 0); - - return err; + return mlx5_core_access_reg(dev, in, sizeof(in), pvlc, + pvlc_size, MLX5_REG_PVLC, 0, 0); } int mlx5_query_port_vl_hw_cap(struct mlx5_core_dev *dev, @@ -339,16 +328,14 @@ int mlx5_set_port_pause(struct mlx5_core_dev *dev, u32 rx_pause, u32 tx_pause) { u32 in[MLX5_ST_SZ_DW(pfcc_reg)]; u32 out[MLX5_ST_SZ_DW(pfcc_reg)]; - int err; memset(in, 0, sizeof(in)); MLX5_SET(pfcc_reg, in, local_port, 1); MLX5_SET(pfcc_reg, in, pptx, tx_pause); MLX5_SET(pfcc_reg, in, pprx, rx_pause); - err = mlx5_core_access_reg(dev, in, sizeof(in), out, - sizeof(out), MLX5_REG_PFCC, 0, 1); - return err; + return mlx5_core_access_reg(dev, in, sizeof(in), out, + sizeof(out), MLX5_REG_PFCC, 0, 1); } EXPORT_SYMBOL_GPL(mlx5_set_port_pause); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c index 8b494b562263..30e2ba3f5f16 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/qp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c @@ -345,6 +345,7 @@ void mlx5_init_qp_table(struct mlx5_core_dev *dev) { struct mlx5_qp_table *table = &dev->priv.qp_table; + memset(table, 0, sizeof(*table)); spin_lock_init(&table->lock); INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); mlx5_qp_debugfs_init(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/srq.c b/drivers/net/ethernet/mellanox/mlx5/core/srq.c index c48f504ccbeb..ffada801976b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/srq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/srq.c @@ -531,6 +531,7 @@ void mlx5_init_srq_table(struct mlx5_core_dev *dev) { struct mlx5_srq_table *table = &dev->priv.srq_table; + memset(table, 0, sizeof(*table)); spin_lock_init(&table->lock); INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/transobj.c b/drivers/net/ethernet/mellanox/mlx5/core/transobj.c index b4c87c7b0cf0..d7068f54e800 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/transobj.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/transobj.c @@ -177,7 +177,7 @@ int mlx5_core_modify_tir(struct mlx5_core_dev *dev, u32 tirn, u32 *in, void mlx5_core_destroy_tir(struct mlx5_core_dev *dev, u32 tirn) { - u32 in[MLX5_ST_SZ_DW(destroy_tir_out)]; + u32 in[MLX5_ST_SZ_DW(destroy_tir_in)]; u32 out[MLX5_ST_SZ_DW(destroy_tir_out)]; memset(in, 0, sizeof(in)); @@ -206,7 +206,7 @@ int mlx5_core_create_tis(struct mlx5_core_dev *dev, u32 *in, int inlen, void mlx5_core_destroy_tis(struct mlx5_core_dev *dev, u32 tisn) { - u32 in[MLX5_ST_SZ_DW(destroy_tis_out)]; + u32 in[MLX5_ST_SZ_DW(destroy_tis_in)]; u32 out[MLX5_ST_SZ_DW(destroy_tis_out)]; memset(in, 0, sizeof(in)); diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 3e52ee93438c..d448431bbc83 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -868,7 +868,7 @@ static int mlxsw_sx_port_attr_get(struct net_device *dev, struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx; switch (attr->id) { - case SWITCHDEV_ATTR_PORT_PARENT_ID: + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: attr->u.ppid.id_len = sizeof(mlxsw_sx->hw_id); memcpy(&attr->u.ppid.id, &mlxsw_sx->hw_id, attr->u.ppid.id_len); break; diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c index 66d4ab703f45..60f43ec22175 100644 --- a/drivers/net/ethernet/micrel/ks8851.c +++ b/drivers/net/ethernet/micrel/ks8851.c @@ -1601,6 +1601,7 @@ static const struct of_device_id ks8851_match_table[] = { { .compatible = "micrel,ks8851" }, { } }; +MODULE_DEVICE_TABLE(of, ks8851_match_table); static struct spi_driver ks8851_driver = { .driver = { diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index 3fd8ca6d4e7c..36a09d94b368 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -33,4 +33,13 @@ config ENC28J60_WRITEVERIFY Enable the verify after the buffer write useful for debugging purpose. If unsure, say N. +config ENCX24J600 + tristate "ENCX24J600 support" + depends on SPI + ---help--- + Support for the Microchip ENC424J600/624J600 ethernet chip. + + To compile this driver as a module, choose M here. The module will be + called encx24j600. + endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile index 573d4292b9ea..ff78f621b59a 100644 --- a/drivers/net/ethernet/microchip/Makefile +++ b/drivers/net/ethernet/microchip/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_ENC28J60) += enc28j60.o +obj-$(CONFIG_ENCX24J600) += encx24j600.o encx24j600-regmap.o diff --git a/drivers/net/ethernet/microchip/encx24j600-regmap.c b/drivers/net/ethernet/microchip/encx24j600-regmap.c new file mode 100644 index 000000000000..f3bb9055a292 --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600-regmap.c @@ -0,0 +1,513 @@ +/** + * Register map access API - ENCX24J600 support + * + * Copyright 2015 Gridpoint + * + * Author: Jon Ringle <jringle@gridpoint.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "encx24j600_hw.h" + +static inline bool is_bits_set(int value, int mask) +{ + return (value & mask) == mask; +} + +static int encx24j600_switch_bank(struct encx24j600_context *ctx, + int bank) +{ + int ret = 0; + + int bank_opcode = BANK_SELECT(bank); + ret = spi_write(ctx->spi, &bank_opcode, 1); + if (ret == 0) + ctx->bank = bank; + + return ret; +} + +static int encx24j600_cmdn(struct encx24j600_context *ctx, u8 opcode, + const void *buf, size_t len) +{ + struct spi_message m; + struct spi_transfer t[2] = { { .tx_buf = &opcode, .len = 1, }, + { .tx_buf = buf, .len = len }, }; + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + return spi_sync(ctx->spi, &m); +} + +static void regmap_lock_mutex(void *context) +{ + struct encx24j600_context *ctx = context; + mutex_lock(&ctx->mutex); +} + +static void regmap_unlock_mutex(void *context) +{ + struct encx24j600_context *ctx = context; + mutex_unlock(&ctx->mutex); +} + +static int regmap_encx24j600_sfr_read(void *context, u8 reg, u8 *val, + size_t len) +{ + struct encx24j600_context *ctx = context; + u8 banked_reg = reg & ADDR_MASK; + u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); + u8 cmd = RCRU; + int ret = 0; + int i = 0; + u8 tx_buf[2]; + + if (reg < 0x80) { + cmd = RCRCODE | banked_reg; + if ((banked_reg < 0x16) && (ctx->bank != bank)) + ret = encx24j600_switch_bank(ctx, bank); + if (unlikely(ret)) + return ret; + } else { + /* Translate registers that are more effecient using + * 3-byte SPI commands + */ + switch (reg) { + case EGPRDPT: + cmd = RGPRDPT; break; + case EGPWRPT: + cmd = RGPWRPT; break; + case ERXRDPT: + cmd = RRXRDPT; break; + case ERXWRPT: + cmd = RRXWRPT; break; + case EUDARDPT: + cmd = RUDARDPT; break; + case EUDAWRPT: + cmd = RUDAWRPT; break; + case EGPDATA: + case ERXDATA: + case EUDADATA: + default: + return -EINVAL; + } + } + + tx_buf[i++] = cmd; + if (cmd == RCRU) + tx_buf[i++] = reg; + + ret = spi_write_then_read(ctx->spi, tx_buf, i, val, len); + + return ret; +} + +static int regmap_encx24j600_sfr_update(struct encx24j600_context *ctx, + u8 reg, u8 *val, size_t len, + u8 unbanked_cmd, u8 banked_code) +{ + u8 banked_reg = reg & ADDR_MASK; + u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); + u8 cmd = unbanked_cmd; + struct spi_message m; + struct spi_transfer t[3] = { { .tx_buf = &cmd, .len = sizeof(cmd), }, + { .tx_buf = ®, .len = sizeof(reg), }, + { .tx_buf = val, .len = len }, }; + + if (reg < 0x80) { + int ret = 0; + cmd = banked_code | banked_reg; + if ((banked_reg < 0x16) && (ctx->bank != bank)) + ret = encx24j600_switch_bank(ctx, bank); + if (unlikely(ret)) + return ret; + } else { + /* Translate registers that are more effecient using + * 3-byte SPI commands + */ + switch (reg) { + case EGPRDPT: + cmd = WGPRDPT; break; + case EGPWRPT: + cmd = WGPWRPT; break; + case ERXRDPT: + cmd = WRXRDPT; break; + case ERXWRPT: + cmd = WRXWRPT; break; + case EUDARDPT: + cmd = WUDARDPT; break; + case EUDAWRPT: + cmd = WUDAWRPT; break; + case EGPDATA: + case ERXDATA: + case EUDADATA: + default: + return -EINVAL; + } + } + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + + if (cmd == unbanked_cmd) { + t[1].tx_buf = ® + spi_message_add_tail(&t[1], &m); + } + + spi_message_add_tail(&t[2], &m); + return spi_sync(ctx->spi, &m); +} + +static int regmap_encx24j600_sfr_write(void *context, u8 reg, u8 *val, + size_t len) +{ + struct encx24j600_context *ctx = context; + return regmap_encx24j600_sfr_update(ctx, reg, val, len, WCRU, WCRCODE); +} + +static int regmap_encx24j600_sfr_set_bits(struct encx24j600_context *ctx, + u8 reg, u8 val) +{ + return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFSU, BFSCODE); +} + +static int regmap_encx24j600_sfr_clr_bits(struct encx24j600_context *ctx, + u8 reg, u8 val) +{ + return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFCU, BFCCODE); +} + +static int regmap_encx24j600_reg_update_bits(void *context, unsigned int reg, + unsigned int mask, + unsigned int val) +{ + struct encx24j600_context *ctx = context; + + int ret = 0; + unsigned int set_mask = mask & val; + unsigned int clr_mask = mask & ~val; + + if ((reg >= 0x40 && reg < 0x6c) || reg >= 0x80) + return -EINVAL; + + if (set_mask & 0xff) + ret = regmap_encx24j600_sfr_set_bits(ctx, reg, set_mask); + + set_mask = (set_mask & 0xff00) >> 8; + + if ((set_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_set_bits(ctx, reg + 1, set_mask); + + if ((clr_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_clr_bits(ctx, reg, clr_mask); + + clr_mask = (clr_mask & 0xff00) >> 8; + + if ((clr_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_clr_bits(ctx, reg + 1, clr_mask); + + return ret; +} + +int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, + size_t count) +{ + struct encx24j600_context *ctx = context; + + if (reg < 0xc0) + return encx24j600_cmdn(ctx, reg, data, count); + else + /* SPI 1-byte command. Ignore data */ + return spi_write(ctx->spi, ®, 1); +} +EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_write); + +int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count) +{ + struct encx24j600_context *ctx = context; + + if (reg == RBSEL && count > 1) + count = 1; + + return spi_write_then_read(ctx->spi, ®, sizeof(reg), data, count); +} +EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_read); + +static int regmap_encx24j600_write(void *context, const void *data, + size_t len) +{ + u8 *dout = (u8 *)data; + u8 reg = dout[0]; + ++dout; + --len; + + if (reg > 0xa0) + return regmap_encx24j600_spi_write(context, reg, dout, len); + + if (len > 2) + return -EINVAL; + + return regmap_encx24j600_sfr_write(context, reg, dout, len); +} + +static int regmap_encx24j600_read(void *context, + const void *reg_buf, size_t reg_size, + void *val, size_t val_size) +{ + u8 reg = *(const u8 *)reg_buf; + + if (reg_size != 1) { + pr_err("%s: reg=%02x reg_size=%zu\n", __func__, reg, reg_size); + return -EINVAL; + } + + if (reg > 0xa0) + return regmap_encx24j600_spi_read(context, reg, val, val_size); + + if (val_size > 2) { + pr_err("%s: reg=%02x val_size=%zu\n", __func__, reg, val_size); + return -EINVAL; + } + + return regmap_encx24j600_sfr_read(context, reg, val, val_size); +} + +static bool encx24j600_regmap_readable(struct device *dev, unsigned int reg) +{ + if ((reg < 0x36) || + ((reg >= 0x40) && (reg < 0x4c)) || + ((reg >= 0x52) && (reg < 0x56)) || + ((reg >= 0x60) && (reg < 0x66)) || + ((reg >= 0x68) && (reg < 0x80)) || + ((reg >= 0x86) && (reg < 0x92)) || + (reg == 0xc8)) + return true; + else + return false; +} + +static bool encx24j600_regmap_writeable(struct device *dev, unsigned int reg) +{ + if ((reg < 0x12) || + ((reg >= 0x14) && (reg < 0x1a)) || + ((reg >= 0x1c) && (reg < 0x36)) || + ((reg >= 0x40) && (reg < 0x4c)) || + ((reg >= 0x52) && (reg < 0x56)) || + ((reg >= 0x60) && (reg < 0x68)) || + ((reg >= 0x6c) && (reg < 0x80)) || + ((reg >= 0x86) && (reg < 0x92)) || + ((reg >= 0xc0) && (reg < 0xc8)) || + ((reg >= 0xca) && (reg < 0xf0))) + return true; + else + return false; +} + +static bool encx24j600_regmap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ERXHEAD: + case EDMACS: + case ETXSTAT: + case ETXWIRE: + case ECON1: /* Can be modified via single byte cmds */ + case ECON2: /* Can be modified via single byte cmds */ + case ESTAT: + case EIR: /* Can be modified via single byte cmds */ + case MIRD: + case MISTAT: + return true; + default: + break; + } + + return false; +} + +static bool encx24j600_regmap_precious(struct device *dev, unsigned int reg) +{ + /* single byte cmds are precious */ + if (((reg >= 0xc0) && (reg < 0xc8)) || + ((reg >= 0xca) && (reg < 0xf0))) + return true; + else + return false; +} + +static int regmap_encx24j600_phy_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct encx24j600_context *ctx = context; + int ret; + unsigned int mistat; + + reg = MIREGADR_VAL | (reg & PHREG_MASK); + ret = regmap_write(ctx->regmap, MIREGADR, reg); + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MICMD, MIIRD); + if (unlikely(ret)) + goto err_out; + + usleep_range(26, 100); + while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && + (mistat & BUSY)) + cpu_relax(); + + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MICMD, 0); + if (unlikely(ret)) + goto err_out; + + ret = regmap_read(ctx->regmap, MIRD, val); + +err_out: + if (ret) + pr_err("%s: error %d reading reg %02x\n", __func__, ret, + reg & PHREG_MASK); + + return ret; +} + +static int regmap_encx24j600_phy_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct encx24j600_context *ctx = context; + int ret; + unsigned int mistat; + + reg = MIREGADR_VAL | (reg & PHREG_MASK); + ret = regmap_write(ctx->regmap, MIREGADR, reg); + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MIWR, val); + if (unlikely(ret)) + goto err_out; + + usleep_range(26, 100); + while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && + (mistat & BUSY)) + cpu_relax(); + +err_out: + if (ret) + pr_err("%s: error %d writing reg %02x=%04x\n", __func__, ret, + reg & PHREG_MASK, val); + + return ret; +} + +static bool encx24j600_phymap_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHCON1: + case PHSTAT1: + case PHANA: + case PHANLPA: + case PHANE: + case PHCON2: + case PHSTAT2: + case PHSTAT3: + return true; + default: + return false; + } +} + +static bool encx24j600_phymap_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHCON1: + case PHCON2: + case PHANA: + return true; + case PHSTAT1: + case PHSTAT2: + case PHSTAT3: + case PHANLPA: + case PHANE: + default: + return false; + } +} + +static bool encx24j600_phymap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHSTAT1: + case PHSTAT2: + case PHSTAT3: + case PHANLPA: + case PHANE: + case PHCON2: + return true; + default: + return false; + } +} + +static struct regmap_config regcfg = { + .name = "reg", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xee, + .reg_stride = 2, + .cache_type = REGCACHE_RBTREE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .readable_reg = encx24j600_regmap_readable, + .writeable_reg = encx24j600_regmap_writeable, + .volatile_reg = encx24j600_regmap_volatile, + .precious_reg = encx24j600_regmap_precious, + .lock = regmap_lock_mutex, + .unlock = regmap_unlock_mutex, +}; + +static struct regmap_bus regmap_encx24j600 = { + .write = regmap_encx24j600_write, + .read = regmap_encx24j600_read, + .reg_update_bits = regmap_encx24j600_reg_update_bits, +}; + +static struct regmap_config phycfg = { + .name = "phy", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0x1f, + .cache_type = REGCACHE_RBTREE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .readable_reg = encx24j600_phymap_readable, + .writeable_reg = encx24j600_phymap_writeable, + .volatile_reg = encx24j600_phymap_volatile, +}; +static struct regmap_bus phymap_encx24j600 = { + .reg_write = regmap_encx24j600_phy_reg_write, + .reg_read = regmap_encx24j600_phy_reg_read, +}; + +void devm_regmap_init_encx24j600(struct device *dev, + struct encx24j600_context *ctx) +{ + mutex_init(&ctx->mutex); + regcfg.lock_arg = ctx; + ctx->regmap = devm_regmap_init(dev, ®map_encx24j600, ctx, ®cfg); + ctx->phymap = devm_regmap_init(dev, &phymap_encx24j600, ctx, &phycfg); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_encx24j600); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c new file mode 100644 index 000000000000..e1329d9c2acc --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600.c @@ -0,0 +1,1127 @@ +/** + * Microchip ENCX24J600 ethernet driver + * + * Copyright (C) 2015 Gridpoint + * Author: Jon Ringle <jringle@gridpoint.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/regmap.h> +#include <linux/skbuff.h> +#include <linux/spi/spi.h> + +#include "encx24j600_hw.h" + +#define DRV_NAME "encx24j600" +#define DRV_VERSION "1.0" + +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +/* SRAM memory layout: + * + * 0x0000-0x05ff TX buffers 1.5KB (1*1536) reside in the GP area in SRAM + * 0x0600-0x5fff RX buffers 22.5KB (15*1536) reside in the RX area in SRAM + */ +#define ENC_TX_BUF_START 0x0000U +#define ENC_RX_BUF_START 0x0600U +#define ENC_RX_BUF_END 0x5fffU +#define ENC_SRAM_SIZE 0x6000U + +enum { + RXFILTER_NORMAL, + RXFILTER_MULTI, + RXFILTER_PROMISC +}; + +struct encx24j600_priv { + struct net_device *ndev; + struct mutex lock; /* device access lock */ + struct encx24j600_context ctx; + struct sk_buff *tx_skb; + struct task_struct *kworker_task; + struct kthread_worker kworker; + struct kthread_work tx_work; + struct kthread_work setrx_work; + u16 next_packet; + bool hw_enabled; + bool full_duplex; + bool autoneg; + u16 speed; + int rxfilter; + u32 msg_enable; +}; + +static void dump_packet(const char *msg, int len, const char *data) +{ + pr_debug(DRV_NAME ": %s - packet len:%d\n", msg, len); + print_hex_dump_bytes("pk data: ", DUMP_PREFIX_OFFSET, data, len); +} + +static void encx24j600_dump_rsv(struct encx24j600_priv *priv, const char *msg, + struct rsv *rsv) +{ + struct net_device *dev = priv->ndev; + + netdev_info(dev, "RX packet Len:%d\n", rsv->len); + netdev_dbg(dev, "%s - NextPk: 0x%04x\n", msg, + rsv->next_packet); + netdev_dbg(dev, "RxOK: %d, DribbleNibble: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXOK), + RSV_GETBIT(rsv->rxstat, RSV_DRIBBLENIBBLE)); + netdev_dbg(dev, "CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_CRCERROR), + RSV_GETBIT(rsv->rxstat, RSV_LENCHECKERR), + RSV_GETBIT(rsv->rxstat, RSV_LENOUTOFRANGE)); + netdev_dbg(dev, "Multicast: %d, Broadcast: %d, LongDropEvent: %d, CarrierEvent: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXMULTICAST), + RSV_GETBIT(rsv->rxstat, RSV_RXBROADCAST), + RSV_GETBIT(rsv->rxstat, RSV_RXLONGEVDROPEV), + RSV_GETBIT(rsv->rxstat, RSV_CARRIEREV)); + netdev_dbg(dev, "ControlFrame: %d, PauseFrame: %d, UnknownOp: %d, VLanTagFrame: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXCONTROLFRAME), + RSV_GETBIT(rsv->rxstat, RSV_RXPAUSEFRAME), + RSV_GETBIT(rsv->rxstat, RSV_RXUNKNOWNOPCODE), + RSV_GETBIT(rsv->rxstat, RSV_RXTYPEVLAN)); +} + +static u16 encx24j600_read_reg(struct encx24j600_priv *priv, u8 reg) +{ + struct net_device *dev = priv->ndev; + unsigned int val = 0; + int ret = regmap_read(priv->ctx.regmap, reg, &val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d reading reg %02x\n", + __func__, ret, reg); + return val; +} + +static void encx24j600_write_reg(struct encx24j600_priv *priv, u8 reg, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.regmap, reg, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", + __func__, ret, reg, val); +} + +static void encx24j600_update_reg(struct encx24j600_priv *priv, u8 reg, + u16 mask, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_update_bits(priv->ctx.regmap, reg, mask, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d updating reg %02x=%04x~%04x\n", + __func__, ret, reg, val, mask); +} + +static u16 encx24j600_read_phy(struct encx24j600_priv *priv, u8 reg) +{ + struct net_device *dev = priv->ndev; + unsigned int val = 0; + int ret = regmap_read(priv->ctx.phymap, reg, &val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d reading %02x\n", + __func__, ret, reg); + return val; +} + +static void encx24j600_write_phy(struct encx24j600_priv *priv, u8 reg, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.phymap, reg, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", + __func__, ret, reg, val); +} + +static void encx24j600_clr_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) +{ + encx24j600_update_reg(priv, reg, mask, 0); +} + +static void encx24j600_set_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) +{ + encx24j600_update_reg(priv, reg, mask, mask); +} + +static void encx24j600_cmd(struct encx24j600_priv *priv, u8 cmd) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.regmap, cmd, 0); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d with cmd %02x\n", + __func__, ret, cmd); +} + +static int encx24j600_raw_read(struct encx24j600_priv *priv, u8 reg, u8 *data, + size_t count) +{ + int ret; + mutex_lock(&priv->ctx.mutex); + ret = regmap_encx24j600_spi_read(&priv->ctx, reg, data, count); + mutex_unlock(&priv->ctx.mutex); + + return ret; +} + +static int encx24j600_raw_write(struct encx24j600_priv *priv, u8 reg, + const u8 *data, size_t count) +{ + int ret; + mutex_lock(&priv->ctx.mutex); + ret = regmap_encx24j600_spi_write(&priv->ctx, reg, data, count); + mutex_unlock(&priv->ctx.mutex); + + return ret; +} + +static void encx24j600_update_phcon1(struct encx24j600_priv *priv) +{ + u16 phcon1 = encx24j600_read_phy(priv, PHCON1); + if (priv->autoneg == AUTONEG_ENABLE) { + phcon1 |= ANEN | RENEG; + } else { + phcon1 &= ~ANEN; + if (priv->speed == SPEED_100) + phcon1 |= SPD100; + else + phcon1 &= ~SPD100; + + if (priv->full_duplex) + phcon1 |= PFULDPX; + else + phcon1 &= ~PFULDPX; + } + encx24j600_write_phy(priv, PHCON1, phcon1); +} + +/* Waits for autonegotiation to complete. */ +static int encx24j600_wait_for_autoneg(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + unsigned long timeout = jiffies + msecs_to_jiffies(2000); + u16 phstat1; + u16 estat; + int ret = 0; + + phstat1 = encx24j600_read_phy(priv, PHSTAT1); + while ((phstat1 & ANDONE) == 0) { + if (time_after(jiffies, timeout)) { + u16 phstat3; + + netif_notice(priv, drv, dev, "timeout waiting for autoneg done\n"); + + priv->autoneg = AUTONEG_DISABLE; + phstat3 = encx24j600_read_phy(priv, PHSTAT3); + priv->speed = (phstat3 & PHY3SPD100) + ? SPEED_100 : SPEED_10; + priv->full_duplex = (phstat3 & PHY3DPX) ? 1 : 0; + encx24j600_update_phcon1(priv); + netif_notice(priv, drv, dev, "Using parallel detection: %s/%s", + priv->speed == SPEED_100 ? "100" : "10", + priv->full_duplex ? "Full" : "Half"); + + return -ETIMEDOUT; + } + cpu_relax(); + phstat1 = encx24j600_read_phy(priv, PHSTAT1); + } + + estat = encx24j600_read_reg(priv, ESTAT); + if (estat & PHYDPX) { + encx24j600_set_bits(priv, MACON2, FULDPX); + encx24j600_write_reg(priv, MABBIPG, 0x15); + } else { + encx24j600_clr_bits(priv, MACON2, FULDPX); + encx24j600_write_reg(priv, MABBIPG, 0x12); + /* Max retransmittions attempt */ + encx24j600_write_reg(priv, MACLCON, 0x370f); + } + + return ret; +} + +/* Access the PHY to determine link status */ +static void encx24j600_check_link_status(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + u16 estat; + + estat = encx24j600_read_reg(priv, ESTAT); + + if (estat & PHYLNK) { + if (priv->autoneg == AUTONEG_ENABLE) + encx24j600_wait_for_autoneg(priv); + + netif_carrier_on(dev); + netif_info(priv, ifup, dev, "link up\n"); + } else { + netif_info(priv, ifdown, dev, "link down\n"); + + /* Re-enable autoneg since we won't know what we might be + * connected to when the link is brought back up again. + */ + priv->autoneg = AUTONEG_ENABLE; + priv->full_duplex = true; + priv->speed = SPEED_100; + netif_carrier_off(dev); + } +} + +static void encx24j600_int_link_handler(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + + netif_dbg(priv, intr, dev, "%s", __func__); + encx24j600_check_link_status(priv); + encx24j600_clr_bits(priv, EIR, LINKIF); +} + +static void encx24j600_tx_complete(struct encx24j600_priv *priv, bool err) +{ + struct net_device *dev = priv->ndev; + + if (!priv->tx_skb) { + BUG(); + return; + } + + mutex_lock(&priv->lock); + + if (err) + dev->stats.tx_errors++; + else + dev->stats.tx_packets++; + + dev->stats.tx_bytes += priv->tx_skb->len; + + encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); + + netif_dbg(priv, tx_done, dev, "TX Done%s\n", err ? ": Err" : ""); + + dev_kfree_skb(priv->tx_skb); + priv->tx_skb = NULL; + + netif_wake_queue(dev); + + mutex_unlock(&priv->lock); +} + +static int encx24j600_receive_packet(struct encx24j600_priv *priv, + struct rsv *rsv) +{ + struct net_device *dev = priv->ndev; + struct sk_buff *skb = netdev_alloc_skb(dev, rsv->len + NET_IP_ALIGN); + if (!skb) { + pr_err_ratelimited("RX: OOM: packet dropped\n"); + dev->stats.rx_dropped++; + return -ENOMEM; + } + skb_reserve(skb, NET_IP_ALIGN); + encx24j600_raw_read(priv, RRXDATA, skb_put(skb, rsv->len), rsv->len); + + if (netif_msg_pktdata(priv)) + dump_packet("RX", skb->len, skb->data); + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_COMPLETE; + + /* Maintain stats */ + dev->stats.rx_packets++; + dev->stats.rx_bytes += rsv->len; + priv->next_packet = rsv->next_packet; + + netif_rx(skb); + + return 0; +} + +static void encx24j600_rx_packets(struct encx24j600_priv *priv, u8 packet_count) +{ + struct net_device *dev = priv->ndev; + + while (packet_count--) { + struct rsv rsv; + u16 newrxtail; + + encx24j600_write_reg(priv, ERXRDPT, priv->next_packet); + encx24j600_raw_read(priv, RRXDATA, (u8 *)&rsv, sizeof(rsv)); + + if (netif_msg_rx_status(priv)) + encx24j600_dump_rsv(priv, __func__, &rsv); + + if (!RSV_GETBIT(rsv.rxstat, RSV_RXOK) || + (rsv.len > MAX_FRAMELEN)) { + netif_err(priv, rx_err, dev, "RX Error %04x\n", + rsv.rxstat); + dev->stats.rx_errors++; + + if (RSV_GETBIT(rsv.rxstat, RSV_CRCERROR)) + dev->stats.rx_crc_errors++; + if (RSV_GETBIT(rsv.rxstat, RSV_LENCHECKERR)) + dev->stats.rx_frame_errors++; + if (rsv.len > MAX_FRAMELEN) + dev->stats.rx_over_errors++; + } else { + encx24j600_receive_packet(priv, &rsv); + } + + newrxtail = priv->next_packet - 2; + if (newrxtail == ENC_RX_BUF_START) + newrxtail = SRAM_SIZE - 2; + + encx24j600_cmd(priv, SETPKTDEC); + encx24j600_write_reg(priv, ERXTAIL, newrxtail); + } +} + +static irqreturn_t encx24j600_isr(int irq, void *dev_id) +{ + struct encx24j600_priv *priv = dev_id; + struct net_device *dev = priv->ndev; + int eir; + + /* Clear interrupts */ + encx24j600_cmd(priv, CLREIE); + + eir = encx24j600_read_reg(priv, EIR); + + if (eir & LINKIF) + encx24j600_int_link_handler(priv); + + if (eir & TXIF) + encx24j600_tx_complete(priv, false); + + if (eir & TXABTIF) + encx24j600_tx_complete(priv, true); + + if (eir & RXABTIF) { + if (eir & PCFULIF) { + /* Packet counter is full */ + netif_err(priv, rx_err, dev, "Packet counter full\n"); + } + dev->stats.rx_dropped++; + encx24j600_clr_bits(priv, EIR, RXABTIF); + } + + if (eir & PKTIF) { + u8 packet_count; + + mutex_lock(&priv->lock); + + packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; + while (packet_count) { + encx24j600_rx_packets(priv, packet_count); + packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; + } + + mutex_unlock(&priv->lock); + } + + /* Enable interrupts */ + encx24j600_cmd(priv, SETEIE); + + return IRQ_HANDLED; +} + +static int encx24j600_soft_reset(struct encx24j600_priv *priv) +{ + int ret = 0; + int timeout; + u16 eudast; + + /* Write and verify a test value to EUDAST */ + regcache_cache_bypass(priv->ctx.regmap, true); + timeout = 10; + do { + encx24j600_write_reg(priv, EUDAST, EUDAST_TEST_VAL); + eudast = encx24j600_read_reg(priv, EUDAST); + usleep_range(25, 100); + } while ((eudast != EUDAST_TEST_VAL) && --timeout); + regcache_cache_bypass(priv->ctx.regmap, false); + + if (timeout == 0) { + ret = -ETIMEDOUT; + goto err_out; + } + + /* Wait for CLKRDY to become set */ + timeout = 10; + while (!(encx24j600_read_reg(priv, ESTAT) & CLKRDY) && --timeout) + usleep_range(25, 100); + + if (timeout == 0) { + ret = -ETIMEDOUT; + goto err_out; + } + + /* Issue a System Reset command */ + encx24j600_cmd(priv, SETETHRST); + usleep_range(25, 100); + + /* Confirm that EUDAST has 0000h after system reset */ + if (encx24j600_read_reg(priv, EUDAST) != 0) { + ret = -EINVAL; + goto err_out; + } + + /* Wait for PHY register and status bits to become available */ + usleep_range(256, 1000); + +err_out: + return ret; +} + +static int encx24j600_hw_reset(struct encx24j600_priv *priv) +{ + int ret; + + mutex_lock(&priv->lock); + ret = encx24j600_soft_reset(priv); + mutex_unlock(&priv->lock); + + return ret; +} + +static void encx24j600_reset_hw_tx(struct encx24j600_priv *priv) +{ + encx24j600_set_bits(priv, ECON2, TXRST); + encx24j600_clr_bits(priv, ECON2, TXRST); +} + +static void encx24j600_hw_init_tx(struct encx24j600_priv *priv) +{ + /* Reset TX */ + encx24j600_reset_hw_tx(priv); + + /* Clear the TXIF flag if were previously set */ + encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); + + /* Write the Tx Buffer pointer */ + encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); +} + +static void encx24j600_hw_init_rx(struct encx24j600_priv *priv) +{ + encx24j600_cmd(priv, DISABLERX); + + /* Set up RX packet start address in the SRAM */ + encx24j600_write_reg(priv, ERXST, ENC_RX_BUF_START); + + /* Preload the RX Data pointer to the beginning of the RX area */ + encx24j600_write_reg(priv, ERXRDPT, ENC_RX_BUF_START); + + priv->next_packet = ENC_RX_BUF_START; + + /* Set up RX end address in the SRAM */ + encx24j600_write_reg(priv, ERXTAIL, ENC_SRAM_SIZE - 2); + + /* Reset the user data pointers */ + encx24j600_write_reg(priv, EUDAST, ENC_SRAM_SIZE); + encx24j600_write_reg(priv, EUDAND, ENC_SRAM_SIZE + 1); + + /* Set Max Frame length */ + encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); +} + +static void encx24j600_dump_config(struct encx24j600_priv *priv, + const char *msg) +{ + pr_info(DRV_NAME ": %s\n", msg); + + /* CHIP configuration */ + pr_info(DRV_NAME " ECON1: %04X\n", encx24j600_read_reg(priv, ECON1)); + pr_info(DRV_NAME " ECON2: %04X\n", encx24j600_read_reg(priv, ECON2)); + pr_info(DRV_NAME " ERXFCON: %04X\n", encx24j600_read_reg(priv, + ERXFCON)); + pr_info(DRV_NAME " ESTAT: %04X\n", encx24j600_read_reg(priv, ESTAT)); + pr_info(DRV_NAME " EIR: %04X\n", encx24j600_read_reg(priv, EIR)); + pr_info(DRV_NAME " EIDLED: %04X\n", encx24j600_read_reg(priv, EIDLED)); + + /* MAC layer configuration */ + pr_info(DRV_NAME " MACON1: %04X\n", encx24j600_read_reg(priv, MACON1)); + pr_info(DRV_NAME " MACON2: %04X\n", encx24j600_read_reg(priv, MACON2)); + pr_info(DRV_NAME " MAIPG: %04X\n", encx24j600_read_reg(priv, MAIPG)); + pr_info(DRV_NAME " MACLCON: %04X\n", encx24j600_read_reg(priv, + MACLCON)); + pr_info(DRV_NAME " MABBIPG: %04X\n", encx24j600_read_reg(priv, + MABBIPG)); + + /* PHY configuation */ + pr_info(DRV_NAME " PHCON1: %04X\n", encx24j600_read_phy(priv, PHCON1)); + pr_info(DRV_NAME " PHCON2: %04X\n", encx24j600_read_phy(priv, PHCON2)); + pr_info(DRV_NAME " PHANA: %04X\n", encx24j600_read_phy(priv, PHANA)); + pr_info(DRV_NAME " PHANLPA: %04X\n", encx24j600_read_phy(priv, + PHANLPA)); + pr_info(DRV_NAME " PHANE: %04X\n", encx24j600_read_phy(priv, PHANE)); + pr_info(DRV_NAME " PHSTAT1: %04X\n", encx24j600_read_phy(priv, + PHSTAT1)); + pr_info(DRV_NAME " PHSTAT2: %04X\n", encx24j600_read_phy(priv, + PHSTAT2)); + pr_info(DRV_NAME " PHSTAT3: %04X\n", encx24j600_read_phy(priv, + PHSTAT3)); +} + +static void encx24j600_set_rxfilter_mode(struct encx24j600_priv *priv) +{ + switch (priv->rxfilter) { + case RXFILTER_PROMISC: + encx24j600_set_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | MCEN | NOTMEEN); + break; + case RXFILTER_MULTI: + encx24j600_clr_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN | MCEN); + break; + case RXFILTER_NORMAL: + default: + encx24j600_clr_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN); + break; + } +} + +static int encx24j600_hw_init(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + int ret = 0; + u16 eidled; + u16 macon2; + + priv->hw_enabled = false; + + eidled = encx24j600_read_reg(priv, EIDLED); + if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) { + ret = -EINVAL; + goto err_out; + } + + netif_info(priv, drv, dev, "Silicon rev ID: 0x%02x\n", + (eidled & REVID_MASK) >> REVID_SHIFT); + + /* PHY Leds: link status, + * LEDA: Link + transmit/receive events + * LEDB: Link State + colision events + */ + encx24j600_update_reg(priv, EIDLED, 0xbc00, 0xbc00); + + /* Loopback disabled */ + encx24j600_write_reg(priv, MACON1, 0x9); + + /* interpacket gap value */ + encx24j600_write_reg(priv, MAIPG, 0x0c12); + + /* Write the auto negotiation pattern */ + encx24j600_write_phy(priv, PHANA, PHANA_DEFAULT); + + encx24j600_update_phcon1(priv); + encx24j600_check_link_status(priv); + + macon2 = MACON2_RSV1 | TXCRCEN | PADCFG0 | PADCFG2 | MACON2_DEFER; + if ((priv->autoneg == AUTONEG_DISABLE) && priv->full_duplex) + macon2 |= FULDPX; + + encx24j600_set_bits(priv, MACON2, macon2); + + priv->rxfilter = RXFILTER_NORMAL; + encx24j600_set_rxfilter_mode(priv); + + /* Program the Maximum frame length */ + encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); + + /* Init Tx pointers */ + encx24j600_hw_init_tx(priv); + + /* Init Rx pointers */ + encx24j600_hw_init_rx(priv); + + if (netif_msg_hw(priv)) + encx24j600_dump_config(priv, "Hw is initialized"); + +err_out: + return ret; +} + +static void encx24j600_hw_enable(struct encx24j600_priv *priv) +{ + /* Clear the interrupt flags in case was set */ + encx24j600_clr_bits(priv, EIR, (PCFULIF | RXABTIF | TXABTIF | TXIF | + PKTIF | LINKIF)); + + /* Enable the interrupts */ + encx24j600_write_reg(priv, EIE, (PCFULIE | RXABTIE | TXABTIE | TXIE | + PKTIE | LINKIE | INTIE)); + + /* Enable RX */ + encx24j600_cmd(priv, ENABLERX); + + priv->hw_enabled = true; +} + +static void encx24j600_hw_disable(struct encx24j600_priv *priv) +{ + /* Disable all interrupts */ + encx24j600_write_reg(priv, EIE, 0); + + /* Disable RX */ + encx24j600_cmd(priv, DISABLERX); + + priv->hw_enabled = false; +} + +static int encx24j600_setlink(struct net_device *dev, u8 autoneg, u16 speed, + u8 duplex) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + int ret = 0; + + if (!priv->hw_enabled) { + /* link is in low power mode now; duplex setting + * will take effect on next encx24j600_hw_init() + */ + if (speed == SPEED_10 || speed == SPEED_100) { + priv->autoneg = (autoneg == AUTONEG_ENABLE); + priv->full_duplex = (duplex == DUPLEX_FULL); + priv->speed = (speed == SPEED_100); + } else { + netif_warn(priv, link, dev, "unsupported link speed setting\n"); + /*speeds other than SPEED_10 and SPEED_100 */ + /*are not supported by chip */ + ret = -EOPNOTSUPP; + } + } else { + netif_warn(priv, link, dev, "Warning: hw must be disabled to set link mode\n"); + ret = -EBUSY; + } + return ret; +} + +static void encx24j600_hw_get_macaddr(struct encx24j600_priv *priv, + unsigned char *ethaddr) +{ + unsigned short val; + + val = encx24j600_read_reg(priv, MAADR1); + + ethaddr[0] = val & 0x00ff; + ethaddr[1] = (val & 0xff00) >> 8; + + val = encx24j600_read_reg(priv, MAADR2); + + ethaddr[2] = val & 0x00ffU; + ethaddr[3] = (val & 0xff00U) >> 8; + + val = encx24j600_read_reg(priv, MAADR3); + + ethaddr[4] = val & 0x00ffU; + ethaddr[5] = (val & 0xff00U) >> 8; +} + +/* Program the hardware MAC address from dev->dev_addr.*/ +static int encx24j600_set_hw_macaddr(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + if (priv->hw_enabled) { + netif_info(priv, drv, dev, "Hardware must be disabled to set Mac address\n"); + return -EBUSY; + } + + mutex_lock(&priv->lock); + + netif_info(priv, drv, dev, "%s: Setting MAC address to %pM\n", + dev->name, dev->dev_addr); + + encx24j600_write_reg(priv, MAADR3, (dev->dev_addr[4] | + dev->dev_addr[5] << 8)); + encx24j600_write_reg(priv, MAADR2, (dev->dev_addr[2] | + dev->dev_addr[3] << 8)); + encx24j600_write_reg(priv, MAADR1, (dev->dev_addr[0] | + dev->dev_addr[1] << 8)); + + mutex_unlock(&priv->lock); + + return 0; +} + +/* Store the new hardware address in dev->dev_addr, and update the MAC.*/ +static int encx24j600_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *address = addr; + + if (netif_running(dev)) + return -EBUSY; + if (!is_valid_ether_addr(address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + return encx24j600_set_hw_macaddr(dev); +} + +static int encx24j600_open(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + int ret = request_threaded_irq(priv->ctx.spi->irq, NULL, encx24j600_isr, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + DRV_NAME, priv); + if (unlikely(ret < 0)) { + netdev_err(dev, "request irq %d failed (ret = %d)\n", + priv->ctx.spi->irq, ret); + return ret; + } + + encx24j600_hw_disable(priv); + encx24j600_hw_init(priv); + encx24j600_hw_enable(priv); + netif_start_queue(dev); + + return 0; +} + +static int encx24j600_stop(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + free_irq(priv->ctx.spi->irq, priv); + return 0; +} + +static void encx24j600_setrx_proc(struct kthread_work *ws) +{ + struct encx24j600_priv *priv = + container_of(ws, struct encx24j600_priv, setrx_work); + + mutex_lock(&priv->lock); + encx24j600_set_rxfilter_mode(priv); + mutex_unlock(&priv->lock); +} + +static void encx24j600_set_multicast_list(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + int oldfilter = priv->rxfilter; + + if (dev->flags & IFF_PROMISC) { + netif_dbg(priv, link, dev, "promiscuous mode\n"); + priv->rxfilter = RXFILTER_PROMISC; + } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) { + netif_dbg(priv, link, dev, "%smulticast mode\n", + (dev->flags & IFF_ALLMULTI) ? "all-" : ""); + priv->rxfilter = RXFILTER_MULTI; + } else { + netif_dbg(priv, link, dev, "normal mode\n"); + priv->rxfilter = RXFILTER_NORMAL; + } + + if (oldfilter != priv->rxfilter) + queue_kthread_work(&priv->kworker, &priv->setrx_work); +} + +static void encx24j600_hw_tx(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + netif_info(priv, tx_queued, dev, "TX Packet Len:%d\n", + priv->tx_skb->len); + + if (netif_msg_pktdata(priv)) + dump_packet("TX", priv->tx_skb->len, priv->tx_skb->data); + + if (encx24j600_read_reg(priv, EIR) & TXABTIF) + /* Last transmition aborted due to error. Reset TX interface */ + encx24j600_reset_hw_tx(priv); + + /* Clear the TXIF flag if were previously set */ + encx24j600_clr_bits(priv, EIR, TXIF); + + /* Set the data pointer to the TX buffer address in the SRAM */ + encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); + + /* Copy the packet into the SRAM */ + encx24j600_raw_write(priv, WGPDATA, (u8 *)priv->tx_skb->data, + priv->tx_skb->len); + + /* Program the Tx buffer start pointer */ + encx24j600_write_reg(priv, ETXST, ENC_TX_BUF_START); + + /* Program the packet length */ + encx24j600_write_reg(priv, ETXLEN, priv->tx_skb->len); + + /* Start the transmission */ + encx24j600_cmd(priv, SETTXRTS); +} + +static void encx24j600_tx_proc(struct kthread_work *ws) +{ + struct encx24j600_priv *priv = + container_of(ws, struct encx24j600_priv, tx_work); + + mutex_lock(&priv->lock); + encx24j600_hw_tx(priv); + mutex_unlock(&priv->lock); +} + +static netdev_tx_t encx24j600_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + + /* save the timestamp */ + dev->trans_start = jiffies; + + /* Remember the skb for deferred processing */ + priv->tx_skb = skb; + + queue_kthread_work(&priv->kworker, &priv->tx_work); + + return NETDEV_TX_OK; +} + +/* Deal with a transmit timeout */ +static void encx24j600_tx_timeout(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_err(priv, tx_err, dev, "TX timeout at %ld, latency %ld\n", + jiffies, jiffies - dev->trans_start); + + dev->stats.tx_errors++; + netif_wake_queue(dev); + return; +} + +static int encx24j600_get_regs_len(struct net_device *dev) +{ + return SFR_REG_COUNT; +} + +static void encx24j600_get_regs(struct net_device *dev, + struct ethtool_regs *regs, void *p) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + u16 *buff = p; + u8 reg; + + regs->version = 1; + mutex_lock(&priv->lock); + for (reg = 0; reg < SFR_REG_COUNT; reg += 2) { + unsigned int val = 0; + /* ignore errors for unreadable registers */ + regmap_read(priv->ctx.regmap, reg, &val); + buff[reg] = val & 0xffff; + } + mutex_unlock(&priv->lock); +} + +static void encx24j600_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(dev->dev.parent), + sizeof(info->bus_info)); +} + +static int encx24j600_get_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + cmd->transceiver = XCVR_INTERNAL; + cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_TP; + + ethtool_cmd_speed_set(cmd, priv->speed); + cmd->duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + cmd->port = PORT_TP; + cmd->autoneg = priv->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; + + return 0; +} + +static int encx24j600_set_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + return encx24j600_setlink(dev, cmd->autoneg, + ethtool_cmd_speed(cmd), cmd->duplex); +} + +static u32 encx24j600_get_msglevel(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + return priv->msg_enable; +} + +static void encx24j600_set_msglevel(struct net_device *dev, u32 val) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + priv->msg_enable = val; +} + +static const struct ethtool_ops encx24j600_ethtool_ops = { + .get_settings = encx24j600_get_settings, + .set_settings = encx24j600_set_settings, + .get_drvinfo = encx24j600_get_drvinfo, + .get_msglevel = encx24j600_get_msglevel, + .set_msglevel = encx24j600_set_msglevel, + .get_regs_len = encx24j600_get_regs_len, + .get_regs = encx24j600_get_regs, +}; + +static const struct net_device_ops encx24j600_netdev_ops = { + .ndo_open = encx24j600_open, + .ndo_stop = encx24j600_stop, + .ndo_start_xmit = encx24j600_tx, + .ndo_set_rx_mode = encx24j600_set_multicast_list, + .ndo_set_mac_address = encx24j600_set_mac_address, + .ndo_tx_timeout = encx24j600_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +static int encx24j600_spi_probe(struct spi_device *spi) +{ + int ret; + + struct net_device *ndev; + struct encx24j600_priv *priv; + + ndev = alloc_etherdev(sizeof(struct encx24j600_priv)); + + if (!ndev) { + ret = -ENOMEM; + goto error_out; + } + + priv = netdev_priv(ndev); + spi_set_drvdata(spi, priv); + dev_set_drvdata(&spi->dev, priv); + SET_NETDEV_DEV(ndev, &spi->dev); + + priv->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); + priv->ndev = ndev; + + /* Default configuration PHY configuration */ + priv->full_duplex = true; + priv->autoneg = AUTONEG_ENABLE; + priv->speed = SPEED_100; + + priv->ctx.spi = spi; + devm_regmap_init_encx24j600(&spi->dev, &priv->ctx); + ndev->irq = spi->irq; + ndev->netdev_ops = &encx24j600_netdev_ops; + + mutex_init(&priv->lock); + + /* Reset device and check if it is connected */ + if (encx24j600_hw_reset(priv)) { + netif_err(priv, probe, ndev, + DRV_NAME ": Chip is not detected\n"); + ret = -EIO; + goto out_free; + } + + /* Initialize the device HW to the consistent state */ + if (encx24j600_hw_init(priv)) { + netif_err(priv, probe, ndev, + DRV_NAME ": HW initialization error\n"); + ret = -EIO; + goto out_free; + } + + init_kthread_worker(&priv->kworker); + init_kthread_work(&priv->tx_work, encx24j600_tx_proc); + init_kthread_work(&priv->setrx_work, encx24j600_setrx_proc); + + priv->kworker_task = kthread_run(kthread_worker_fn, &priv->kworker, + "encx24j600"); + + if (IS_ERR(priv->kworker_task)) { + ret = PTR_ERR(priv->kworker_task); + goto out_free; + } + + /* Get the MAC address from the chip */ + encx24j600_hw_get_macaddr(priv, ndev->dev_addr); + + ndev->ethtool_ops = &encx24j600_ethtool_ops; + + ret = register_netdev(ndev); + if (unlikely(ret)) { + netif_err(priv, probe, ndev, "Error %d initializing card encx24j600 card\n", + ret); + goto out_free; + } + + netif_info(priv, drv, priv->ndev, "MAC address %pM\n", ndev->dev_addr); + + return ret; + +out_free: + free_netdev(ndev); + +error_out: + return ret; +} + +static int encx24j600_spi_remove(struct spi_device *spi) +{ + struct encx24j600_priv *priv = dev_get_drvdata(&spi->dev); + + unregister_netdev(priv->ndev); + + free_netdev(priv->ndev); + + return 0; +} + +static const struct spi_device_id encx24j600_spi_id_table = { + .name = "encx24j600" +}; + +static struct spi_driver encx24j600_spi_net_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .bus = &spi_bus_type, + }, + .probe = encx24j600_spi_probe, + .remove = encx24j600_spi_remove, + .id_table = &encx24j600_spi_id_table, +}; + +static int __init encx24j600_init(void) +{ + return spi_register_driver(&encx24j600_spi_net_driver); +} +module_init(encx24j600_init); + +static void encx24j600_exit(void) +{ + spi_unregister_driver(&encx24j600_spi_net_driver); +} +module_exit(encx24j600_exit); + +MODULE_DESCRIPTION(DRV_NAME " ethernet driver"); +MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/net/ethernet/microchip/encx24j600_hw.h b/drivers/net/ethernet/microchip/encx24j600_hw.h new file mode 100644 index 000000000000..4be73d5553f8 --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600_hw.h @@ -0,0 +1,437 @@ +/** + * encx24j600_hw.h: Register definitions + * + */ + +#ifndef _ENCX24J600_HW_H +#define _ENCX24J600_HW_H + +struct encx24j600_context { + struct spi_device *spi; + struct regmap *regmap; + struct regmap *phymap; + struct mutex mutex; /* mutex to protect access to regmap */ + int bank; +}; + +void devm_regmap_init_encx24j600(struct device *dev, + struct encx24j600_context *ctx); + +/* Single-byte instructions */ +#define BANK_SELECT(bank) (0xC0 | ((bank & (BANK_MASK >> BANK_SHIFT)) << 1)) +#define B0SEL 0xC0 /* Bank 0 Select */ +#define B1SEL 0xC2 /* Bank 1 Select */ +#define B2SEL 0xC4 /* Bank 2 Select */ +#define B3SEL 0xC6 /* Bank 3 Select */ +#define SETETHRST 0xCA /* System Reset */ +#define FCDISABLE 0xE0 /* Flow Control Disable */ +#define FCSINGLE 0xE2 /* Flow Control Single */ +#define FCMULTIPLE 0xE4 /* Flow Control Multiple */ +#define FCCLEAR 0xE6 /* Flow Control Clear */ +#define SETPKTDEC 0xCC /* Decrement Packet Counter */ +#define DMASTOP 0xD2 /* DMA Stop */ +#define DMACKSUM 0xD8 /* DMA Start Checksum */ +#define DMACKSUMS 0xDA /* DMA Start Checksum with Seed */ +#define DMACOPY 0xDC /* DMA Start Copy */ +#define DMACOPYS 0xDE /* DMA Start Copy and Checksum with Seed */ +#define SETTXRTS 0xD4 /* Request Packet Transmission */ +#define ENABLERX 0xE8 /* Enable RX */ +#define DISABLERX 0xEA /* Disable RX */ +#define SETEIE 0xEC /* Enable Interrupts */ +#define CLREIE 0xEE /* Disable Interrupts */ + +/* Two byte instructions */ +#define RBSEL 0xC8 /* Read Bank Select */ + +/* Three byte instructions */ +#define WGPRDPT 0x60 /* Write EGPRDPT */ +#define RGPRDPT 0x62 /* Read EGPRDPT */ +#define WRXRDPT 0x64 /* Write ERXRDPT */ +#define RRXRDPT 0x66 /* Read ERXRDPT */ +#define WUDARDPT 0x68 /* Write EUDARDPT */ +#define RUDARDPT 0x6A /* Read EUDARDPT */ +#define WGPWRPT 0x6C /* Write EGPWRPT */ +#define RGPWRPT 0x6E /* Read EGPWRPT */ +#define WRXWRPT 0x70 /* Write ERXWRPT */ +#define RRXWRPT 0x72 /* Read ERXWRPT */ +#define WUDAWRPT 0x74 /* Write EUDAWRPT */ +#define RUDAWRPT 0x76 /* Read EUDAWRPT */ + +/* n byte instructions */ +#define RCRCODE 0x00 +#define WCRCODE 0x40 +#define BFSCODE 0x80 +#define BFCCODE 0xA0 +#define RCR(addr) (RCRCODE | (addr & ADDR_MASK)) /* Read Control Register */ +#define WCR(addr) (WCRCODE | (addr & ADDR_MASK)) /* Write Control Register */ +#define RCRU 0x20 /* Read Control Register Unbanked */ +#define WCRU 0x22 /* Write Control Register Unbanked */ +#define BFS(addr) (BFSCODE | (addr & ADDR_MASK)) /* Bit Field Set */ +#define BFC(addr) (BFCCODE | (addr & ADDR_MASK)) /* Bit Field Clear */ +#define BFSU 0x24 /* Bit Field Set Unbanked */ +#define BFCU 0x26 /* Bit Field Clear Unbanked */ +#define RGPDATA 0x28 /* Read EGPDATA */ +#define WGPDATA 0x2A /* Write EGPDATA */ +#define RRXDATA 0x2C /* Read ERXDATA */ +#define WRXDATA 0x2E /* Write ERXDATA */ +#define RUDADATA 0x30 /* Read EUDADATA */ +#define WUDADATA 0x32 /* Write EUDADATA */ + +#define SFR_REG_COUNT 0xA0 + +/* ENC424J600 Control Registers + * Control register definitions are a combination of address + * and bank number + * - Register address (bits 0-4) + * - Bank number (bits 5-6) + */ +#define ADDR_MASK 0x1F +#define BANK_MASK 0x60 +#define BANK_SHIFT 5 + +/* All-bank registers */ +#define EUDAST 0x16 +#define EUDAND 0x18 +#define ESTAT 0x1A +#define EIR 0x1C +#define ECON1 0x1E + +/* Bank 0 registers */ +#define ETXST (0x00 | 0x00) +#define ETXLEN (0x02 | 0x00) +#define ERXST (0x04 | 0x00) +#define ERXTAIL (0x06 | 0x00) +#define ERXHEAD (0x08 | 0x00) +#define EDMAST (0x0A | 0x00) +#define EDMALEN (0x0C | 0x00) +#define EDMADST (0x0E | 0x00) +#define EDMACS (0x10 | 0x00) +#define ETXSTAT (0x12 | 0x00) +#define ETXWIRE (0x14 | 0x00) + +/* Bank 1 registers */ +#define EHT1 (0x00 | 0x20) +#define EHT2 (0x02 | 0x20) +#define EHT3 (0x04 | 0x20) +#define EHT4 (0x06 | 0x20) +#define EPMM1 (0x08 | 0x20) +#define EPMM2 (0x0A | 0x20) +#define EPMM3 (0x0C | 0x20) +#define EPMM4 (0x0E | 0x20) +#define EPMCS (0x10 | 0x20) +#define EPMO (0x12 | 0x20) +#define ERXFCON (0x14 | 0x20) + +/* Bank 2 registers */ +#define MACON1 (0x00 | 0x40) +#define MACON2 (0x02 | 0x40) +#define MABBIPG (0x04 | 0x40) +#define MAIPG (0x06 | 0x40) +#define MACLCON (0x08 | 0x40) +#define MAMXFL (0x0A | 0x40) +#define MICMD (0x12 | 0x40) +#define MIREGADR (0x14 | 0x40) + +/* Bank 3 registers */ +#define MAADR3 (0x00 | 0x60) +#define MAADR2 (0x02 | 0x60) +#define MAADR1 (0x04 | 0x60) +#define MIWR (0x06 | 0x60) +#define MIRD (0x08 | 0x60) +#define MISTAT (0x0A | 0x60) +#define EPAUS (0x0C | 0x60) +#define ECON2 (0x0E | 0x60) +#define ERXWM (0x10 | 0x60) +#define EIE (0x12 | 0x60) +#define EIDLED (0x14 | 0x60) + +/* Unbanked registers */ +#define EGPDATA (0x00 | 0x80) +#define ERXDATA (0x02 | 0x80) +#define EUDADATA (0x04 | 0x80) +#define EGPRDPT (0x06 | 0x80) +#define EGPWRPT (0x08 | 0x80) +#define ERXRDPT (0x0A | 0x80) +#define ERXWRPT (0x0C | 0x80) +#define EUDARDPT (0x0E | 0x80) +#define EUDAWRPT (0x10 | 0x80) + + +/* Register bit definitions */ +/* ESTAT */ +#define INT (1 << 15) +#define FCIDLE (1 << 14) +#define RXBUSY (1 << 13) +#define CLKRDY (1 << 12) +#define PHYDPX (1 << 10) +#define PHYLNK (1 << 8) + +/* EIR */ +#define CRYPTEN (1 << 15) +#define MODEXIF (1 << 14) +#define HASHIF (1 << 13) +#define AESIF (1 << 12) +#define LINKIF (1 << 11) +#define PKTIF (1 << 6) +#define DMAIF (1 << 5) +#define TXIF (1 << 3) +#define TXABTIF (1 << 2) +#define RXABTIF (1 << 1) +#define PCFULIF (1 << 0) + +/* ECON1 */ +#define MODEXST (1 << 15) +#define HASHEN (1 << 14) +#define HASHOP (1 << 13) +#define HASHLST (1 << 12) +#define AESST (1 << 11) +#define AESOP1 (1 << 10) +#define AESOP0 (1 << 9) +#define PKTDEC (1 << 8) +#define FCOP1 (1 << 7) +#define FCOP0 (1 << 6) +#define DMAST (1 << 5) +#define DMACPY (1 << 4) +#define DMACSSD (1 << 3) +#define DMANOCS (1 << 2) +#define TXRTS (1 << 1) +#define RXEN (1 << 0) + +/* ETXSTAT */ +#define LATECOL (1 << 10) +#define MAXCOL (1 << 9) +#define EXDEFER (1 << 8) +#define ETXSTATL_DEFER (1 << 7) +#define CRCBAD (1 << 4) +#define COLCNT_MASK 0xF + +/* ERXFCON */ +#define HTEN (1 << 15) +#define MPEN (1 << 14) +#define NOTPM (1 << 12) +#define PMEN3 (1 << 11) +#define PMEN2 (1 << 10) +#define PMEN1 (1 << 9) +#define PMEN0 (1 << 8) +#define CRCEEN (1 << 7) +#define CRCEN (1 << 6) +#define RUNTEEN (1 << 5) +#define RUNTEN (1 << 4) +#define UCEN (1 << 3) +#define NOTMEEN (1 << 2) +#define MCEN (1 << 1) +#define BCEN (1 << 0) + +/* MACON1 */ +#define LOOPBK (1 << 4) +#define RXPAUS (1 << 2) +#define PASSALL (1 << 1) + +/* MACON2 */ +#define MACON2_DEFER (1 << 14) +#define BPEN (1 << 13) +#define NOBKOFF (1 << 12) +#define PADCFG2 (1 << 7) +#define PADCFG1 (1 << 6) +#define PADCFG0 (1 << 5) +#define TXCRCEN (1 << 4) +#define PHDREN (1 << 3) +#define HFRMEN (1 << 2) +#define MACON2_RSV1 (1 << 1) +#define FULDPX (1 << 0) + +/* MAIPG */ +/* value of the high byte is given by the reserved bits, + * value of the low byte is recomended setting of the + * IPG parameter. + */ +#define MAIPGH_VAL 0x0C +#define MAIPGL_VAL 0x12 + +/* MIREGADRH */ +#define MIREGADR_VAL (1 << 8) + +/* MIREGADRL */ +#define PHREG_MASK 0x1F + +/* MICMD */ +#define MIISCAN (1 << 1) +#define MIIRD (1 << 0) + +/* MISTAT */ +#define NVALID (1 << 2) +#define SCAN (1 << 1) +#define BUSY (1 << 0) + +/* ECON2 */ +#define ETHEN (1 << 15) +#define STRCH (1 << 14) +#define TXMAC (1 << 13) +#define SHA1MD5 (1 << 12) +#define COCON3 (1 << 11) +#define COCON2 (1 << 10) +#define COCON1 (1 << 9) +#define COCON0 (1 << 8) +#define AUTOFC (1 << 7) +#define TXRST (1 << 6) +#define RXRST (1 << 5) +#define ETHRST (1 << 4) +#define MODLEN1 (1 << 3) +#define MODLEN0 (1 << 2) +#define AESLEN1 (1 << 1) +#define AESLEN0 (1 << 0) + +/* EIE */ +#define INTIE (1 << 15) +#define MODEXIE (1 << 14) +#define HASHIE (1 << 13) +#define AESIE (1 << 12) +#define LINKIE (1 << 11) +#define PKTIE (1 << 6) +#define DMAIE (1 << 5) +#define TXIE (1 << 3) +#define TXABTIE (1 << 2) +#define RXABTIE (1 << 1) +#define PCFULIE (1 << 0) + +/* EIDLED */ +#define LACFG3 (1 << 15) +#define LACFG2 (1 << 14) +#define LACFG1 (1 << 13) +#define LACFG0 (1 << 12) +#define LBCFG3 (1 << 11) +#define LBCFG2 (1 << 10) +#define LBCFG1 (1 << 9) +#define LBCFG0 (1 << 8) +#define DEVID_SHIFT 5 +#define DEVID_MASK (0x7 << DEVID_SHIFT) +#define REVID_SHIFT 0 +#define REVID_MASK (0x1F << REVID_SHIFT) + +/* PHY registers */ +#define PHCON1 0x00 +#define PHSTAT1 0x01 +#define PHANA 0x04 +#define PHANLPA 0x05 +#define PHANE 0x06 +#define PHCON2 0x11 +#define PHSTAT2 0x1B +#define PHSTAT3 0x1F + +/* PHCON1 */ +#define PRST (1 << 15) +#define PLOOPBK (1 << 14) +#define SPD100 (1 << 13) +#define ANEN (1 << 12) +#define PSLEEP (1 << 11) +#define RENEG (1 << 9) +#define PFULDPX (1 << 8) + +/* PHSTAT1 */ +#define FULL100 (1 << 14) +#define HALF100 (1 << 13) +#define FULL10 (1 << 12) +#define HALF10 (1 << 11) +#define ANDONE (1 << 5) +#define LRFAULT (1 << 4) +#define ANABLE (1 << 3) +#define LLSTAT (1 << 2) +#define EXTREGS (1 << 0) + +/* PHSTAT2 */ +#define PLRITY (1 << 4) + +/* PHSTAT3 */ +#define PHY3SPD100 (1 << 3) +#define PHY3DPX (1 << 4) +#define SPDDPX_SHIFT 2 +#define SPDDPX_MASK (0x7 << SPDDPX_SHIFT) + +/* PHANA */ +/* Default value for PHY initialization*/ +#define PHANA_DEFAULT 0x05E1 + +/* PHANE */ +#define PDFLT (1 << 4) +#define LPARCD (1 << 1) +#define LPANABL (1 << 0) + +#define EUDAST_TEST_VAL 0x1234 + +#define TSV_SIZE 7 + +#define ENCX24J600_DEV_ID 0x1 + +/* Configuration */ + +/* Led is on when the link is present and driven low + * temporarily when packet is TX'd or RX'd + */ +#define LED_A_SETTINGS 0xC + +/* Led is on if the link is in 100 Mbps mode */ +#define LED_B_SETTINGS 0x8 + +/* maximum ethernet frame length + * Currently not used as a limit anywhere + * (we're using the "huge frame enable" feature of + * enc424j600). + */ +#define MAX_FRAMELEN 1518 + +/* Size in bytes of the receive buffer in enc424j600. + * Must be word aligned (even). + */ +#define RX_BUFFER_SIZE (15 * MAX_FRAMELEN) + +/* Start of the general purpose area in sram */ +#define SRAM_GP_START 0x0 + +/* SRAM size */ +#define SRAM_SIZE 0x6000 + +/* Start of the receive buffer */ +#define ERXST_VAL (SRAM_SIZE - RX_BUFFER_SIZE) + +#define RSV_RXLONGEVDROPEV 16 +#define RSV_CARRIEREV 18 +#define RSV_CRCERROR 20 +#define RSV_LENCHECKERR 21 +#define RSV_LENOUTOFRANGE 22 +#define RSV_RXOK 23 +#define RSV_RXMULTICAST 24 +#define RSV_RXBROADCAST 25 +#define RSV_DRIBBLENIBBLE 26 +#define RSV_RXCONTROLFRAME 27 +#define RSV_RXPAUSEFRAME 28 +#define RSV_RXUNKNOWNOPCODE 29 +#define RSV_RXTYPEVLAN 30 + +#define RSV_RUNTFILTERMATCH 31 +#define RSV_NOTMEFILTERMATCH 32 +#define RSV_HASHFILTERMATCH 33 +#define RSV_MAGICPKTFILTERMATCH 34 +#define RSV_PTRNMTCHFILTERMATCH 35 +#define RSV_UNICASTFILTERMATCH 36 + +#define RSV_SIZE 8 +#define RSV_BITMASK(x) (1 << ((x) - 16)) +#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0) + +struct rsv { + u16 next_packet; + u16 len; + u32 rxstat; +}; + +/* Put RX buffer at 0 as suggested by the Errata datasheet */ + +#define RXSTART_INIT ERXST_VAL +#define RXEND_INIT 0x5FFF + +int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, + size_t count); +int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count); + + +#endif diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index becbb5f1f5a7..a10c928bbd6b 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -552,6 +552,7 @@ static const struct of_device_id moxart_mac_match[] = { { .compatible = "moxa,moxart-mac" }, { } }; +MODULE_DEVICE_TABLE(of, moxart_mac_match); static struct platform_driver moxart_mac_driver = { .probe = moxart_mac_probe, diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 06bcc734fe8d..d6696cfa11d2 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -536,6 +536,7 @@ struct qlcnic_hardware_context { u8 extend_lb_time; u8 phys_port_id[ETH_ALEN]; u8 lb_mode; + u8 vxlan_port_count; u16 vxlan_port; struct device *hwmon_dev; u32 post_mode; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 8b08b20e8b30..d4481454b5f8 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -483,11 +483,17 @@ static void qlcnic_add_vxlan_port(struct net_device *netdev, /* Adapter supports only one VXLAN port. Use very first port * for enabling offload */ - if (!qlcnic_encap_rx_offload(adapter) || ahw->vxlan_port) + if (!qlcnic_encap_rx_offload(adapter)) return; + if (!ahw->vxlan_port_count) { + ahw->vxlan_port_count = 1; + ahw->vxlan_port = ntohs(port); + adapter->flags |= QLCNIC_ADD_VXLAN_PORT; + return; + } + if (ahw->vxlan_port == ntohs(port)) + ahw->vxlan_port_count++; - ahw->vxlan_port = ntohs(port); - adapter->flags |= QLCNIC_ADD_VXLAN_PORT; } static void qlcnic_del_vxlan_port(struct net_device *netdev, @@ -496,11 +502,13 @@ static void qlcnic_del_vxlan_port(struct net_device *netdev, struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_hardware_context *ahw = adapter->ahw; - if (!qlcnic_encap_rx_offload(adapter) || !ahw->vxlan_port || + if (!qlcnic_encap_rx_offload(adapter) || !ahw->vxlan_port_count || (ahw->vxlan_port != ntohs(port))) return; - adapter->flags |= QLCNIC_DEL_VXLAN_PORT; + ahw->vxlan_port_count--; + if (!ahw->vxlan_port_count) + adapter->flags |= QLCNIC_DEL_VXLAN_PORT; } static netdev_features_t qlcnic_features_check(struct sk_buff *skb, diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index d79e33b3c191..deae10d7426d 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -157,6 +157,7 @@ enum { NWayAdvert = 0x66, /* MII ADVERTISE */ NWayLPAR = 0x68, /* MII LPA */ NWayExpansion = 0x6A, /* MII Expansion */ + TxDmaOkLowDesc = 0x82, /* Low 16 bit address of a Tx descriptor. */ Config5 = 0xD8, /* Config5 */ TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */ RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */ @@ -174,7 +175,7 @@ enum { LastFrag = (1 << 28), /* Final segment of a packet */ LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */ MSSShift = 16, /* MSS value position */ - MSSMask = 0xfff, /* MSS value: 11 bits */ + MSSMask = 0x7ff, /* MSS value: 11 bits */ TxError = (1 << 23), /* Tx error summary */ RxError = (1 << 20), /* Rx error summary */ IPCS = (1 << 18), /* Calculate IP checksum */ @@ -341,6 +342,7 @@ struct cp_private { unsigned tx_tail; struct cp_desc *tx_ring; struct sk_buff *tx_skb[CP_TX_RING_SIZE]; + u32 tx_opts[CP_TX_RING_SIZE]; unsigned rx_buf_sz; unsigned wol_enabled : 1; /* Is Wake-on-LAN enabled? */ @@ -665,7 +667,7 @@ static void cp_tx (struct cp_private *cp) BUG_ON(!skb); dma_unmap_single(&cp->pdev->dev, le64_to_cpu(txd->addr), - le32_to_cpu(txd->opts1) & 0xffff, + cp->tx_opts[tx_tail] & 0xffff, PCI_DMA_TODEVICE); if (status & LastFrag) { @@ -733,7 +735,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, { struct cp_private *cp = netdev_priv(dev); unsigned entry; - u32 eor, flags; + u32 eor, opts1; unsigned long intr_flags; __le32 opts2; int mss = 0; @@ -752,7 +754,28 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; mss = skb_shinfo(skb)->gso_size; + if (mss > MSSMask) { + WARN_ONCE(1, "Net bug: GSO size %d too large for 8139CP\n", + mss); + goto out_dma_error; + } + opts2 = cpu_to_le32(cp_tx_vlan_tag(skb)); + opts1 = DescOwn; + if (mss) + opts1 |= LargeSend | (mss << MSSShift); + else if (skb->ip_summed == CHECKSUM_PARTIAL) { + const struct iphdr *ip = ip_hdr(skb); + if (ip->protocol == IPPROTO_TCP) + opts1 |= IPCS | TCPCS; + else if (ip->protocol == IPPROTO_UDP) + opts1 |= IPCS | UDPCS; + else { + WARN_ONCE(1, + "Net bug: asked to checksum invalid Legacy IP packet\n"); + goto out_dma_error; + } + } if (skb_shinfo(skb)->nr_frags == 0) { struct cp_desc *txd = &cp->tx_ring[entry]; @@ -768,31 +791,20 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, txd->addr = cpu_to_le64(mapping); wmb(); - flags = eor | len | DescOwn | FirstFrag | LastFrag; - - if (mss) - flags |= LargeSend | ((mss & MSSMask) << MSSShift); - else if (skb->ip_summed == CHECKSUM_PARTIAL) { - const struct iphdr *ip = ip_hdr(skb); - if (ip->protocol == IPPROTO_TCP) - flags |= IPCS | TCPCS; - else if (ip->protocol == IPPROTO_UDP) - flags |= IPCS | UDPCS; - else - WARN_ON(1); /* we need a WARN() */ - } + opts1 |= eor | len | FirstFrag | LastFrag; - txd->opts1 = cpu_to_le32(flags); + txd->opts1 = cpu_to_le32(opts1); wmb(); cp->tx_skb[entry] = skb; - entry = NEXT_TX(entry); + cp->tx_opts[entry] = opts1; + netif_dbg(cp, tx_queued, cp->dev, "tx queued, slot %d, skblen %d\n", + entry, skb->len); } else { struct cp_desc *txd; - u32 first_len, first_eor; + u32 first_len, first_eor, ctrl; dma_addr_t first_mapping; int frag, first_entry = entry; - const struct iphdr *ip = ip_hdr(skb); /* We must give this initial chunk to the device last. * Otherwise we could race with the device. @@ -805,14 +817,14 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, goto out_dma_error; cp->tx_skb[entry] = skb; - entry = NEXT_TX(entry); for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { const skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag]; u32 len; - u32 ctrl; dma_addr_t mapping; + entry = NEXT_TX(entry); + len = skb_frag_size(this_frag); mapping = dma_map_single(&cp->pdev->dev, skb_frag_address(this_frag), @@ -824,19 +836,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; - ctrl = eor | len | DescOwn; - - if (mss) - ctrl |= LargeSend | - ((mss & MSSMask) << MSSShift); - else if (skb->ip_summed == CHECKSUM_PARTIAL) { - if (ip->protocol == IPPROTO_TCP) - ctrl |= IPCS | TCPCS; - else if (ip->protocol == IPPROTO_UDP) - ctrl |= IPCS | UDPCS; - else - BUG(); - } + ctrl = opts1 | eor | len; if (frag == skb_shinfo(skb)->nr_frags - 1) ctrl |= LastFrag; @@ -849,8 +849,8 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, txd->opts1 = cpu_to_le32(ctrl); wmb(); + cp->tx_opts[entry] = ctrl; cp->tx_skb[entry] = skb; - entry = NEXT_TX(entry); } txd = &cp->tx_ring[first_entry]; @@ -858,27 +858,17 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, txd->addr = cpu_to_le64(first_mapping); wmb(); - if (skb->ip_summed == CHECKSUM_PARTIAL) { - if (ip->protocol == IPPROTO_TCP) - txd->opts1 = cpu_to_le32(first_eor | first_len | - FirstFrag | DescOwn | - IPCS | TCPCS); - else if (ip->protocol == IPPROTO_UDP) - txd->opts1 = cpu_to_le32(first_eor | first_len | - FirstFrag | DescOwn | - IPCS | UDPCS); - else - BUG(); - } else - txd->opts1 = cpu_to_le32(first_eor | first_len | - FirstFrag | DescOwn); + ctrl = opts1 | first_eor | first_len | FirstFrag; + txd->opts1 = cpu_to_le32(ctrl); wmb(); + + cp->tx_opts[first_entry] = ctrl; + netif_dbg(cp, tx_queued, cp->dev, "tx queued, slots %d-%d, skblen %d\n", + first_entry, entry, skb->len); } - cp->tx_head = entry; + cp->tx_head = NEXT_TX(entry); netdev_sent_queue(dev, skb->len); - netif_dbg(cp, tx_queued, cp->dev, "tx queued, slot %d, skblen %d\n", - entry, skb->len); if (TX_BUFFS_AVAIL(cp) <= (MAX_SKB_FRAGS + 1)) netif_stop_queue(dev); @@ -1115,6 +1105,7 @@ static int cp_init_rings (struct cp_private *cp) { memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); cp->tx_ring[CP_TX_RING_SIZE - 1].opts1 = cpu_to_le32(RingEnd); + memset(cp->tx_opts, 0, sizeof(cp->tx_opts)); cp_init_rings_index(cp); @@ -1151,7 +1142,7 @@ static void cp_clean_rings (struct cp_private *cp) desc = cp->rx_ring + i; dma_unmap_single(&cp->pdev->dev,le64_to_cpu(desc->addr), cp->rx_buf_sz, PCI_DMA_FROMDEVICE); - dev_kfree_skb(cp->rx_skb[i]); + dev_kfree_skb_any(cp->rx_skb[i]); } } @@ -1164,7 +1155,7 @@ static void cp_clean_rings (struct cp_private *cp) le32_to_cpu(desc->opts1) & 0xffff, PCI_DMA_TODEVICE); if (le32_to_cpu(desc->opts1) & LastFrag) - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); cp->dev->stats.tx_dropped++; } } @@ -1172,6 +1163,7 @@ static void cp_clean_rings (struct cp_private *cp) memset(cp->rx_ring, 0, sizeof(struct cp_desc) * CP_RX_RING_SIZE); memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); + memset(cp->tx_opts, 0, sizeof(cp->tx_opts)); memset(cp->rx_skb, 0, sizeof(struct sk_buff *) * CP_RX_RING_SIZE); memset(cp->tx_skb, 0, sizeof(struct sk_buff *) * CP_TX_RING_SIZE); @@ -1249,7 +1241,7 @@ static void cp_tx_timeout(struct net_device *dev) { struct cp_private *cp = netdev_priv(dev); unsigned long flags; - int rc; + int rc, i; netdev_warn(dev, "Transmit timeout, status %2x %4x %4x %4x\n", cpr8(Cmd), cpr16(CpCmd), @@ -1257,13 +1249,26 @@ static void cp_tx_timeout(struct net_device *dev) spin_lock_irqsave(&cp->lock, flags); + netif_dbg(cp, tx_err, cp->dev, "TX ring head %d tail %d desc %x\n", + cp->tx_head, cp->tx_tail, cpr16(TxDmaOkLowDesc)); + for (i = 0; i < CP_TX_RING_SIZE; i++) { + netif_dbg(cp, tx_err, cp->dev, + "TX slot %d @%p: %08x (%08x) %08x %llx %p\n", + i, &cp->tx_ring[i], le32_to_cpu(cp->tx_ring[i].opts1), + cp->tx_opts[i], le32_to_cpu(cp->tx_ring[i].opts2), + le64_to_cpu(cp->tx_ring[i].addr), + cp->tx_skb[i]); + } + cp_stop_hw(cp); cp_clean_rings(cp); rc = cp_init_rings(cp); cp_start_hw(cp); - cp_enable_irq(cp); + __cp_set_rx_mode(dev); + cpw16_f(IntrMask, cp_norx_intr_mask); netif_wake_queue(dev); + napi_schedule_irqoff(&cp->napi); spin_unlock_irqrestore(&cp->lock, flags); } @@ -1853,6 +1858,15 @@ static void cp_set_d3_state (struct cp_private *cp) pci_set_power_state (cp->pdev, PCI_D3hot); } +static netdev_features_t cp_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + if (skb_shinfo(skb)->gso_size > MSSMask) + features &= ~NETIF_F_TSO; + + return vlan_features_check(skb, features); +} static const struct net_device_ops cp_netdev_ops = { .ndo_open = cp_open, .ndo_stop = cp_close, @@ -1865,6 +1879,7 @@ static const struct net_device_ops cp_netdev_ops = { .ndo_tx_timeout = cp_tx_timeout, .ndo_set_features = cp_set_features, .ndo_change_mtu = cp_change_mtu, + .ndo_features_check = cp_features_check, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = cp_poll_controller, @@ -1984,12 +1999,12 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) dev->ethtool_ops = &cp_ethtool_ops; dev->watchdog_timeo = TX_TIMEOUT; - dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | + NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; if (pci_using_dac) dev->features |= NETIF_F_HIGHDMA; - /* disabled by default until verified */ dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 2b32e0c5a0b4..b4f21232019a 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -6081,7 +6081,7 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp) { void __iomem *ioaddr = tp->mmio_addr; struct pci_dev *pdev = tp->pci_dev; - u16 rg_saw_cnt; + int rg_saw_cnt; u32 data; static const struct ephy_info e_info_8168h_1[] = { { 0x1e, 0x0800, 0x0001 }, diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h index a157aaaaff6a..0623fff932e4 100644 --- a/drivers/net/ethernet/renesas/ravb.h +++ b/drivers/net/ethernet/renesas/ravb.h @@ -766,6 +766,11 @@ struct ravb_ptp { struct ravb_ptp_perout perout[N_PER_OUT]; }; +enum ravb_chip_id { + RCAR_GEN2, + RCAR_GEN3, +}; + struct ravb_private { struct net_device *ndev; struct platform_device *pdev; @@ -806,6 +811,8 @@ struct ravb_private { int msg_enable; int speed; int duplex; + int emac_irq; + enum ravb_chip_id chip_id; unsigned no_avb_link:1; unsigned avb_link_active_low:1; diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 450899e9cea2..8cc5ec5ed19a 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -201,7 +201,7 @@ static void ravb_ring_free(struct net_device *ndev, int q) if (priv->rx_ring[q]) { ring_size = sizeof(struct ravb_ex_rx_desc) * (priv->num_rx_ring[q] + 1); - dma_free_coherent(NULL, ring_size, priv->rx_ring[q], + dma_free_coherent(ndev->dev.parent, ring_size, priv->rx_ring[q], priv->rx_desc_dma[q]); priv->rx_ring[q] = NULL; } @@ -209,7 +209,7 @@ static void ravb_ring_free(struct net_device *ndev, int q) if (priv->tx_ring[q]) { ring_size = sizeof(struct ravb_tx_desc) * (priv->num_tx_ring[q] * NUM_TX_DESC + 1); - dma_free_coherent(NULL, ring_size, priv->tx_ring[q], + dma_free_coherent(ndev->dev.parent, ring_size, priv->tx_ring[q], priv->tx_desc_dma[q]); priv->tx_ring[q] = NULL; } @@ -240,13 +240,13 @@ static void ravb_ring_format(struct net_device *ndev, int q) rx_desc = &priv->rx_ring[q][i]; /* The size of the buffer should be on 16-byte boundary. */ rx_desc->ds_cc = cpu_to_le16(ALIGN(PKT_BUF_SZ, 16)); - dma_addr = dma_map_single(&ndev->dev, priv->rx_skb[q][i]->data, + dma_addr = dma_map_single(ndev->dev.parent, priv->rx_skb[q][i]->data, ALIGN(PKT_BUF_SZ, 16), DMA_FROM_DEVICE); /* We just set the data size to 0 for a failed mapping which * should prevent DMA from happening... */ - if (dma_mapping_error(&ndev->dev, dma_addr)) + if (dma_mapping_error(ndev->dev.parent, dma_addr)) rx_desc->ds_cc = cpu_to_le16(0); rx_desc->dptr = cpu_to_le32(dma_addr); rx_desc->die_dt = DT_FEMPTY; @@ -309,7 +309,7 @@ static int ravb_ring_init(struct net_device *ndev, int q) /* Allocate all RX descriptors. */ ring_size = sizeof(struct ravb_ex_rx_desc) * (priv->num_rx_ring[q] + 1); - priv->rx_ring[q] = dma_alloc_coherent(NULL, ring_size, + priv->rx_ring[q] = dma_alloc_coherent(ndev->dev.parent, ring_size, &priv->rx_desc_dma[q], GFP_KERNEL); if (!priv->rx_ring[q]) @@ -320,7 +320,7 @@ static int ravb_ring_init(struct net_device *ndev, int q) /* Allocate all TX descriptors. */ ring_size = sizeof(struct ravb_tx_desc) * (priv->num_tx_ring[q] * NUM_TX_DESC + 1); - priv->tx_ring[q] = dma_alloc_coherent(NULL, ring_size, + priv->tx_ring[q] = dma_alloc_coherent(ndev->dev.parent, ring_size, &priv->tx_desc_dma[q], GFP_KERNEL); if (!priv->tx_ring[q]) @@ -443,7 +443,7 @@ static int ravb_tx_free(struct net_device *ndev, int q) size = le16_to_cpu(desc->ds_tagl) & TX_DS; /* Free the original skb. */ if (priv->tx_skb[q][entry / NUM_TX_DESC]) { - dma_unmap_single(&ndev->dev, le32_to_cpu(desc->dptr), + dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr), size, DMA_TO_DEVICE); /* Last packet descriptor? */ if (entry % NUM_TX_DESC == NUM_TX_DESC - 1) { @@ -546,7 +546,7 @@ static bool ravb_rx(struct net_device *ndev, int *quota, int q) skb = priv->rx_skb[q][entry]; priv->rx_skb[q][entry] = NULL; - dma_unmap_single(&ndev->dev, le32_to_cpu(desc->dptr), + dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr), ALIGN(PKT_BUF_SZ, 16), DMA_FROM_DEVICE); get_ts &= (q == RAVB_NC) ? @@ -586,14 +586,14 @@ static bool ravb_rx(struct net_device *ndev, int *quota, int q) if (!skb) break; /* Better luck next round. */ ravb_set_buffer_align(skb); - dma_addr = dma_map_single(&ndev->dev, skb->data, + dma_addr = dma_map_single(ndev->dev.parent, skb->data, le16_to_cpu(desc->ds_cc), DMA_FROM_DEVICE); skb_checksum_none_assert(skb); /* We just set the data size to 0 for a failed mapping * which should prevent DMA from happening... */ - if (dma_mapping_error(&ndev->dev, dma_addr)) + if (dma_mapping_error(ndev->dev.parent, dma_addr)) desc->ds_cc = cpu_to_le16(0); desc->dptr = cpu_to_le32(dma_addr); priv->rx_skb[q][entry] = skb; @@ -889,6 +889,22 @@ static int ravb_phy_init(struct net_device *ndev) return -ENOENT; } + /* This driver only support 10/100Mbit speeds on Gen3 + * at this time. + */ + if (priv->chip_id == RCAR_GEN3) { + int err; + + err = phy_set_max_speed(phydev, SPEED_100); + if (err) { + netdev_err(ndev, "failed to limit PHY to 100Mbit/s\n"); + phy_disconnect(phydev); + return err; + } + + netdev_info(ndev, "limited PHY to 100Mbit/s\n"); + } + netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n", phydev->addr, phydev->irq, phydev->drv->name); @@ -1197,6 +1213,15 @@ static int ravb_open(struct net_device *ndev) goto out_napi_off; } + if (priv->chip_id == RCAR_GEN3) { + error = request_irq(priv->emac_irq, ravb_interrupt, + IRQF_SHARED, ndev->name, ndev); + if (error) { + netdev_err(ndev, "cannot request IRQ\n"); + goto out_free_irq; + } + } + /* Device init */ error = ravb_dmac_init(ndev); if (error) @@ -1220,6 +1245,7 @@ out_ptp_stop: ravb_ptp_stop(ndev); out_free_irq: free_irq(ndev->irq, ndev); + free_irq(priv->emac_irq, ndev); out_napi_off: napi_disable(&priv->napi[RAVB_NC]); napi_disable(&priv->napi[RAVB_BE]); @@ -1300,8 +1326,8 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) entry / NUM_TX_DESC * DPTR_ALIGN; len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data; memcpy(buffer, skb->data, len); - dma_addr = dma_map_single(&ndev->dev, buffer, len, DMA_TO_DEVICE); - if (dma_mapping_error(&ndev->dev, dma_addr)) + dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE); + if (dma_mapping_error(ndev->dev.parent, dma_addr)) goto drop; desc = &priv->tx_ring[q][entry]; @@ -1310,8 +1336,8 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) buffer = skb->data + len; len = skb->len - len; - dma_addr = dma_map_single(&ndev->dev, buffer, len, DMA_TO_DEVICE); - if (dma_mapping_error(&ndev->dev, dma_addr)) + dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE); + if (dma_mapping_error(ndev->dev.parent, dma_addr)) goto unmap; desc++; @@ -1323,7 +1349,7 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) ts_skb = kmalloc(sizeof(*ts_skb), GFP_ATOMIC); if (!ts_skb) { desc--; - dma_unmap_single(&ndev->dev, dma_addr, len, + dma_unmap_single(ndev->dev.parent, dma_addr, len, DMA_TO_DEVICE); goto unmap; } @@ -1358,7 +1384,7 @@ exit: return NETDEV_TX_OK; unmap: - dma_unmap_single(&ndev->dev, le32_to_cpu(desc->dptr), + dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr), le16_to_cpu(desc->ds_tagl), DMA_TO_DEVICE); drop: dev_kfree_skb_any(skb); @@ -1625,10 +1651,20 @@ static int ravb_mdio_release(struct ravb_private *priv) return 0; } +static const struct of_device_id ravb_match_table[] = { + { .compatible = "renesas,etheravb-r8a7790", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,etheravb-r8a7794", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,etheravb-r8a7795", .data = (void *)RCAR_GEN3 }, + { } +}; +MODULE_DEVICE_TABLE(of, ravb_match_table); + static int ravb_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; struct ravb_private *priv; + enum ravb_chip_id chip_id; struct net_device *ndev; int error, irq, q; struct resource *res; @@ -1657,7 +1693,14 @@ static int ravb_probe(struct platform_device *pdev) /* The Ether-specific entries in the device structure. */ ndev->base_addr = res->start; ndev->dma = -1; - irq = platform_get_irq(pdev, 0); + + match = of_match_device(of_match_ptr(ravb_match_table), &pdev->dev); + chip_id = (enum ravb_chip_id)match->data; + + if (chip_id == RCAR_GEN3) + irq = platform_get_irq_byname(pdev, "ch22"); + else + irq = platform_get_irq(pdev, 0); if (irq < 0) { error = irq; goto out_release; @@ -1688,6 +1731,17 @@ static int ravb_probe(struct platform_device *pdev) priv->avb_link_active_low = of_property_read_bool(np, "renesas,ether-link-active-low"); + if (chip_id == RCAR_GEN3) { + irq = platform_get_irq_byname(pdev, "ch24"); + if (irq < 0) { + error = irq; + goto out_release; + } + priv->emac_irq = irq; + } + + priv->chip_id = chip_id; + /* Set function */ ndev->netdev_ops = &ravb_netdev_ops; ndev->ethtool_ops = &ravb_ethtool_ops; @@ -1708,7 +1762,7 @@ static int ravb_probe(struct platform_device *pdev) /* Allocate descriptor base address table */ priv->desc_bat_size = sizeof(struct ravb_desc) * DBAT_ENTRY_NUM; - priv->desc_bat = dma_alloc_coherent(NULL, priv->desc_bat_size, + priv->desc_bat = dma_alloc_coherent(ndev->dev.parent, priv->desc_bat_size, &priv->desc_bat_dma, GFP_KERNEL); if (!priv->desc_bat) { dev_err(&ndev->dev, @@ -1763,7 +1817,7 @@ out_napi_del: netif_napi_del(&priv->napi[RAVB_BE]); ravb_mdio_release(priv); out_dma_free: - dma_free_coherent(NULL, priv->desc_bat_size, priv->desc_bat, + dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat, priv->desc_bat_dma); out_release: if (ndev) @@ -1779,7 +1833,7 @@ static int ravb_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct ravb_private *priv = netdev_priv(ndev); - dma_free_coherent(NULL, priv->desc_bat_size, priv->desc_bat, + dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat, priv->desc_bat_dma); /* Set reset mode */ ravb_write(ndev, CCC_OPC_RESET, CCC); @@ -1818,13 +1872,6 @@ static const struct dev_pm_ops ravb_dev_pm_ops = { #define RAVB_PM_OPS NULL #endif -static const struct of_device_id ravb_match_table[] = { - { .compatible = "renesas,etheravb-r8a7790" }, - { .compatible = "renesas,etheravb-r8a7794" }, - { } -}; -MODULE_DEVICE_TABLE(of, ravb_match_table); - static struct platform_driver ravb_driver = { .probe = ravb_probe, .remove = ravb_remove, diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index 34ac41ac9e61..eafa907965ec 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -152,8 +152,9 @@ struct rocker_fdb_tbl_entry { struct hlist_node entry; u32 key_crc32; /* key */ bool learned; + unsigned long touched; struct rocker_fdb_tbl_key { - u32 pport; + struct rocker_port *rocker_port; u8 addr[ETH_ALEN]; __be16 vlan_id; } key; @@ -220,13 +221,13 @@ struct rocker_port { __be16 internal_vlan_id; int stp_state; u32 brport_flags; + unsigned long ageing_time; bool ctrls[ROCKER_CTRL_MAX]; unsigned long vlan_bitmap[ROCKER_VLAN_BITMAP_LEN]; struct napi_struct napi_tx; struct napi_struct napi_rx; struct rocker_dma_ring_info tx_ring; struct rocker_dma_ring_info rx_ring; - struct list_head trans_mem; }; struct rocker { @@ -246,6 +247,7 @@ struct rocker { u64 flow_tbl_next_cookie; DECLARE_HASHTABLE(group_tbl, 16); spinlock_t group_tbl_lock; /* for group tbl accesses */ + struct timer_list fdb_cleanup_timer; DECLARE_HASHTABLE(fdb_tbl, 16); spinlock_t fdb_tbl_lock; /* for fdb tbl accesses */ unsigned long internal_vlan_bitmap[ROCKER_INTERNAL_VLAN_BITMAP_LEN]; @@ -340,74 +342,63 @@ static bool rocker_port_is_ovsed(const struct rocker_port *rocker_port) #define ROCKER_OP_FLAG_REFRESH BIT(3) static void *__rocker_port_mem_alloc(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, size_t size) { - struct list_head *elem = NULL; + struct switchdev_trans_item *elem = NULL; gfp_t gfp_flags = (flags & ROCKER_OP_FLAG_NOWAIT) ? GFP_ATOMIC : GFP_KERNEL; /* If in transaction prepare phase, allocate the memory - * and enqueue it on a per-port list. If in transaction - * commit phase, dequeue the memory from the per-port list + * and enqueue it on a transaction. If in transaction + * commit phase, dequeue the memory from the transaction * rather than re-allocating the memory. The idea is the * driver code paths for prepare and commit are identical * so the memory allocated in the prepare phase is the * memory used in the commit phase. */ - switch (trans) { - case SWITCHDEV_TRANS_PREPARE: + if (!trans) { + elem = kzalloc(size + sizeof(*elem), gfp_flags); + } else if (switchdev_trans_ph_prepare(trans)) { elem = kzalloc(size + sizeof(*elem), gfp_flags); if (!elem) return NULL; - list_add_tail(elem, &rocker_port->trans_mem); - break; - case SWITCHDEV_TRANS_COMMIT: - BUG_ON(list_empty(&rocker_port->trans_mem)); - elem = rocker_port->trans_mem.next; - list_del_init(elem); - break; - case SWITCHDEV_TRANS_NONE: - elem = kzalloc(size + sizeof(*elem), gfp_flags); - if (elem) - INIT_LIST_HEAD(elem); - break; - default: - break; + switchdev_trans_item_enqueue(trans, elem, kfree, elem); + } else { + elem = switchdev_trans_item_dequeue(trans); } return elem ? elem + 1 : NULL; } static void *rocker_port_kzalloc(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, size_t size) { return __rocker_port_mem_alloc(rocker_port, trans, flags, size); } static void *rocker_port_kcalloc(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, size_t n, size_t size) { return __rocker_port_mem_alloc(rocker_port, trans, flags, n * size); } -static void rocker_port_kfree(enum switchdev_trans trans, const void *mem) +static void rocker_port_kfree(struct switchdev_trans *trans, const void *mem) { - struct list_head *elem; + struct switchdev_trans_item *elem; /* Frees are ignored if in transaction prepare phase. The * memory remains on the per-port list until freed in the * commit phase. */ - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) return; - elem = (struct list_head *)mem - 1; - BUG_ON(!list_empty(elem)); + elem = (struct switchdev_trans_item *) mem - 1; kfree(elem); } @@ -430,7 +421,7 @@ static void rocker_wait_init(struct rocker_wait *wait) } static struct rocker_wait *rocker_wait_create(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags) { struct rocker_wait *wait; @@ -442,7 +433,7 @@ static struct rocker_wait *rocker_wait_create(struct rocker_port *rocker_port, return wait; } -static void rocker_wait_destroy(enum switchdev_trans trans, +static void rocker_wait_destroy(struct switchdev_trans *trans, struct rocker_wait *wait) { rocker_port_kfree(trans, wait); @@ -1408,7 +1399,7 @@ static irqreturn_t rocker_cmd_irq_handler(int irq, void *dev_id) wait = rocker_desc_cookie_ptr_get(desc_info); if (wait->nowait) { rocker_desc_gen_clear(desc_info); - rocker_wait_destroy(SWITCHDEV_TRANS_NONE, wait); + rocker_wait_destroy(NULL, wait); } else { rocker_wait_wake_up(wait); } @@ -1463,7 +1454,7 @@ static int rocker_event_link_change(const struct rocker *rocker, } static int rocker_port_fdb(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, const unsigned char *addr, __be16 vlan_id, int flags); @@ -1496,8 +1487,7 @@ static int rocker_event_mac_vlan_seen(const struct rocker *rocker, rocker_port->stp_state != BR_STATE_FORWARDING) return 0; - return rocker_port_fdb(rocker_port, SWITCHDEV_TRANS_NONE, - addr, vlan_id, flags); + return rocker_port_fdb(rocker_port, NULL, addr, vlan_id, flags); } static int rocker_event_process(const struct rocker *rocker, @@ -1582,7 +1572,7 @@ typedef int (*rocker_cmd_proc_cb_t)(const struct rocker_port *rocker_port, void *priv); static int rocker_cmd_exec(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, rocker_cmd_prep_cb_t prepare, void *prepare_priv, rocker_cmd_proc_cb_t process, void *process_priv) { @@ -1615,7 +1605,7 @@ static int rocker_cmd_exec(struct rocker_port *rocker_port, rocker_desc_cookie_ptr_set(desc_info, wait); - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) rocker_desc_head_set(rocker, &rocker->cmd_ring, desc_info); spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags); @@ -1623,7 +1613,7 @@ static int rocker_cmd_exec(struct rocker_port *rocker_port, if (nowait) return 0; - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) if (!rocker_wait_event_timeout(wait, HZ / 10)) return -EIO; @@ -1875,7 +1865,7 @@ rocker_cmd_set_port_learning_prep(const struct rocker_port *rocker_port, static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port, struct ethtool_cmd *ecmd) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_settings_prep, NULL, rocker_cmd_get_port_settings_ethtool_proc, ecmd); @@ -1884,7 +1874,7 @@ static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port, static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port, unsigned char *macaddr) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_settings_prep, NULL, rocker_cmd_get_port_settings_macaddr_proc, macaddr); @@ -1893,7 +1883,7 @@ static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port, static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port, struct ethtool_cmd *ecmd) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_set_port_settings_ethtool_prep, ecmd, NULL, NULL); } @@ -1901,7 +1891,7 @@ static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port, static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port, unsigned char *macaddr) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_set_port_settings_macaddr_prep, macaddr, NULL, NULL); } @@ -1909,13 +1899,13 @@ static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port, static int rocker_cmd_set_port_settings_mtu(struct rocker_port *rocker_port, int mtu) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_set_port_settings_mtu_prep, &mtu, NULL, NULL); } static int rocker_port_set_learning(struct rocker_port *rocker_port, - enum switchdev_trans trans) + struct switchdev_trans *trans) { return rocker_cmd_exec(rocker_port, trans, 0, rocker_cmd_set_port_learning_prep, @@ -2433,7 +2423,7 @@ rocker_flow_tbl_find(const struct rocker *rocker, } static int rocker_flow_tbl_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_flow_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2449,7 +2439,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port, if (found) { match->cookie = found->cookie; - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); rocker_port_kfree(trans, found); found = match; @@ -2460,7 +2450,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port, found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD; } - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_add(rocker->flow_tbl, &found->entry, found->key_crc32); spin_unlock_irqrestore(&rocker->flow_tbl_lock, lock_flags); @@ -2470,7 +2460,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port, } static int rocker_flow_tbl_del(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_flow_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2486,7 +2476,7 @@ static int rocker_flow_tbl_del(struct rocker_port *rocker_port, found = rocker_flow_tbl_find(rocker, match); if (found) { - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL; } @@ -2506,7 +2496,7 @@ static int rocker_flow_tbl_del(struct rocker_port *rocker_port, } static int rocker_flow_tbl_do(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_flow_tbl_entry *entry) { if (flags & ROCKER_OP_FLAG_REMOVE) @@ -2516,7 +2506,7 @@ static int rocker_flow_tbl_do(struct rocker_port *rocker_port, } static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 in_pport, u32 in_pport_mask, enum rocker_of_dpa_table_id goto_tbl) { @@ -2536,7 +2526,7 @@ static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port, } static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 in_pport, __be16 vlan_id, __be16 vlan_id_mask, enum rocker_of_dpa_table_id goto_tbl, @@ -2562,7 +2552,7 @@ static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port, } static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, u32 in_pport, u32 in_pport_mask, __be16 eth_type, const u8 *eth_dst, const u8 *eth_dst_mask, __be16 vlan_id, @@ -2599,7 +2589,7 @@ static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port, } static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const u8 *eth_dst, const u8 *eth_dst_mask, __be16 vlan_id, u32 tunnel_id, enum rocker_of_dpa_table_id goto_tbl, @@ -2653,7 +2643,7 @@ static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port, } static int rocker_flow_tbl_ucast4_routing(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, __be16 eth_type, __be32 dst, __be32 dst_mask, u32 priority, enum rocker_of_dpa_table_id goto_tbl, @@ -2679,7 +2669,7 @@ static int rocker_flow_tbl_ucast4_routing(struct rocker_port *rocker_port, } static int rocker_flow_tbl_acl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 in_pport, u32 in_pport_mask, const u8 *eth_src, const u8 *eth_src_mask, const u8 *eth_dst, const u8 *eth_dst_mask, @@ -2744,7 +2734,7 @@ rocker_group_tbl_find(const struct rocker *rocker, return NULL; } -static void rocker_group_tbl_entry_free(enum switchdev_trans trans, +static void rocker_group_tbl_entry_free(struct switchdev_trans *trans, struct rocker_group_tbl_entry *entry) { switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) { @@ -2759,7 +2749,7 @@ static void rocker_group_tbl_entry_free(enum switchdev_trans trans, } static int rocker_group_tbl_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_group_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2771,7 +2761,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port, found = rocker_group_tbl_find(rocker, match); if (found) { - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); rocker_group_tbl_entry_free(trans, found); found = match; @@ -2781,7 +2771,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port, found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD; } - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_add(rocker->group_tbl, &found->entry, found->group_id); spin_unlock_irqrestore(&rocker->group_tbl_lock, lock_flags); @@ -2791,7 +2781,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port, } static int rocker_group_tbl_del(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_group_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2804,7 +2794,7 @@ static int rocker_group_tbl_del(struct rocker_port *rocker_port, found = rocker_group_tbl_find(rocker, match); if (found) { - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL; } @@ -2824,7 +2814,7 @@ static int rocker_group_tbl_del(struct rocker_port *rocker_port, } static int rocker_group_tbl_do(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_group_tbl_entry *entry) { if (flags & ROCKER_OP_FLAG_REMOVE) @@ -2834,7 +2824,7 @@ static int rocker_group_tbl_do(struct rocker_port *rocker_port, } static int rocker_group_l2_interface(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id, u32 out_pport, int pop_vlan) { @@ -2851,7 +2841,7 @@ static int rocker_group_l2_interface(struct rocker_port *rocker_port, } static int rocker_group_l2_fan_out(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags, u8 group_count, const u32 *group_ids, u32 group_id) { @@ -2876,7 +2866,7 @@ static int rocker_group_l2_fan_out(struct rocker_port *rocker_port, } static int rocker_group_l2_flood(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id, u8 group_count, const u32 *group_ids, u32 group_id) { @@ -2886,7 +2876,7 @@ static int rocker_group_l2_flood(struct rocker_port *rocker_port, } static int rocker_group_l3_unicast(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 index, const u8 *src_mac, const u8 *dst_mac, __be16 vlan_id, bool ttl_check, u32 pport) { @@ -2922,22 +2912,22 @@ rocker_neigh_tbl_find(const struct rocker *rocker, __be32 ip_addr) } static void _rocker_neigh_add(struct rocker *rocker, - enum switchdev_trans trans, + struct switchdev_trans *trans, struct rocker_neigh_tbl_entry *entry) { - if (trans != SWITCHDEV_TRANS_COMMIT) + if (!switchdev_trans_ph_commit(trans)) entry->index = rocker->neigh_tbl_next_index++; - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) return; entry->ref_count++; hash_add(rocker->neigh_tbl, &entry->entry, be32_to_cpu(entry->ip_addr)); } -static void _rocker_neigh_del(enum switchdev_trans trans, +static void _rocker_neigh_del(struct switchdev_trans *trans, struct rocker_neigh_tbl_entry *entry) { - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) return; if (--entry->ref_count == 0) { hash_del(&entry->entry); @@ -2946,19 +2936,19 @@ static void _rocker_neigh_del(enum switchdev_trans trans, } static void _rocker_neigh_update(struct rocker_neigh_tbl_entry *entry, - enum switchdev_trans trans, + struct switchdev_trans *trans, const u8 *eth_dst, bool ttl_check) { if (eth_dst) { ether_addr_copy(entry->eth_dst, eth_dst); entry->ttl_check = ttl_check; - } else if (trans != SWITCHDEV_TRANS_PREPARE) { + } else if (!switchdev_trans_ph_prepare(trans)) { entry->ref_count++; } } static int rocker_port_ipv4_neigh(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags, __be32 ip_addr, const u8 *eth_dst) { struct rocker *rocker = rocker_port->rocker; @@ -3050,7 +3040,8 @@ err_out: } static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port, - enum switchdev_trans trans, __be32 ip_addr) + struct switchdev_trans *trans, + __be32 ip_addr) { struct net_device *dev = rocker_port->dev; struct neighbour *n = __ipv4_neigh_lookup(dev, (__force u32)ip_addr); @@ -3078,7 +3069,7 @@ static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port, } static int rocker_port_ipv4_nh(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be32 ip_addr, u32 *index) { struct rocker *rocker = rocker_port->rocker; @@ -3137,7 +3128,7 @@ static int rocker_port_ipv4_nh(struct rocker_port *rocker_port, } static int rocker_port_vlan_flood_group(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags, __be16 vlan_id) { struct rocker_port *p; @@ -3186,7 +3177,7 @@ no_ports_in_vlan: } static int rocker_port_vlan_l2_groups(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id, bool pop_vlan) { const struct rocker *rocker = rocker_port->rocker; @@ -3292,7 +3283,7 @@ static struct rocker_ctrl { }; static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { u32 in_pport = rocker_port->pport; @@ -3325,7 +3316,8 @@ static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan_bridge(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, + int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { @@ -3350,7 +3342,7 @@ static int rocker_port_ctrl_vlan_bridge(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan_term(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { u32 in_pport_mask = 0xffffffff; @@ -3374,7 +3366,7 @@ static int rocker_port_ctrl_vlan_term(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { if (ctrl->acl) @@ -3392,7 +3384,7 @@ static int rocker_port_ctrl_vlan(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id) { int err = 0; @@ -3411,7 +3403,7 @@ static int rocker_port_ctrl_vlan_add(struct rocker_port *rocker_port, } static int rocker_port_ctrl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl) { u16 vid; @@ -3430,7 +3422,7 @@ static int rocker_port_ctrl(struct rocker_port *rocker_port, } static int rocker_port_vlan(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, u16 vid) + struct switchdev_trans *trans, int flags, u16 vid) { enum rocker_of_dpa_table_id goto_tbl = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC; @@ -3487,14 +3479,14 @@ static int rocker_port_vlan(struct rocker_port *rocker_port, "Error (%d) port VLAN table\n", err); err_out: - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) change_bit(ntohs(internal_vlan_id), rocker_port->vlan_bitmap); return err; } static int rocker_port_ig_tbl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { enum rocker_of_dpa_table_id goto_tbl; u32 in_pport; @@ -3522,7 +3514,7 @@ static int rocker_port_ig_tbl(struct rocker_port *rocker_port, struct rocker_fdb_learn_work { struct work_struct work; struct rocker_port *rocker_port; - enum switchdev_trans trans; + struct switchdev_trans *trans; int flags; u8 addr[ETH_ALEN]; u16 vid; @@ -3550,7 +3542,7 @@ static void rocker_port_fdb_learn_work(struct work_struct *work) } static int rocker_port_fdb_learn(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const u8 *addr, __be16 vlan_id) { struct rocker_fdb_learn_work *lw; @@ -3592,7 +3584,7 @@ static int rocker_port_fdb_learn(struct rocker_port *rocker_port, ether_addr_copy(lw->addr, addr); lw->vid = rocker_port_vlan_to_vid(rocker_port, vlan_id); - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) rocker_port_kfree(trans, lw); else schedule_work(&lw->work); @@ -3614,7 +3606,7 @@ rocker_fdb_tbl_find(const struct rocker *rocker, } static int rocker_port_fdb(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, const unsigned char *addr, __be16 vlan_id, int flags) { @@ -3629,7 +3621,8 @@ static int rocker_port_fdb(struct rocker_port *rocker_port, return -ENOMEM; fdb->learned = (flags & ROCKER_OP_FLAG_LEARNED); - fdb->key.pport = rocker_port->pport; + fdb->touched = jiffies; + fdb->key.rocker_port = rocker_port; ether_addr_copy(fdb->key.addr, addr); fdb->key.vlan_id = vlan_id; fdb->key_crc32 = crc32(~0, &fdb->key, sizeof(fdb->key)); @@ -3638,13 +3631,17 @@ static int rocker_port_fdb(struct rocker_port *rocker_port, found = rocker_fdb_tbl_find(rocker, fdb); - if (removing && found) { - rocker_port_kfree(trans, fdb); - if (trans != SWITCHDEV_TRANS_PREPARE) - hash_del(&found->entry); - } else if (!removing && !found) { - if (trans != SWITCHDEV_TRANS_PREPARE) - hash_add(rocker->fdb_tbl, &fdb->entry, fdb->key_crc32); + if (found) { + found->touched = jiffies; + if (removing) { + rocker_port_kfree(trans, fdb); + if (!switchdev_trans_ph_prepare(trans)) + hash_del(&found->entry); + } + } else if (!removing) { + if (!switchdev_trans_ph_prepare(trans)) + hash_add(rocker->fdb_tbl, &fdb->entry, + fdb->key_crc32); } spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags); @@ -3662,7 +3659,7 @@ static int rocker_port_fdb(struct rocker_port *rocker_port, } static int rocker_port_fdb_flush(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { struct rocker *rocker = rocker_port->rocker; struct rocker_fdb_tbl_entry *found; @@ -3680,7 +3677,7 @@ static int rocker_port_fdb_flush(struct rocker_port *rocker_port, spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags); hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) { - if (found->key.pport != rocker_port->pport) + if (found->key.rocker_port != rocker_port) continue; if (!found->learned) continue; @@ -3689,7 +3686,7 @@ static int rocker_port_fdb_flush(struct rocker_port *rocker_port, found->key.vlan_id); if (err) goto err_out; - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); } @@ -3699,8 +3696,43 @@ err_out: return err; } +static void rocker_fdb_cleanup(unsigned long data) +{ + struct rocker *rocker = (struct rocker *)data; + struct rocker_port *rocker_port; + struct rocker_fdb_tbl_entry *entry; + struct hlist_node *tmp; + unsigned long next_timer = jiffies + BR_MIN_AGEING_TIME; + unsigned long expires; + unsigned long lock_flags; + int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE | + ROCKER_OP_FLAG_LEARNED; + int bkt; + + spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags); + + hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, entry, entry) { + if (!entry->learned) + continue; + rocker_port = entry->key.rocker_port; + expires = entry->touched + rocker_port->ageing_time; + if (time_before_eq(expires, jiffies)) { + rocker_port_fdb_learn(rocker_port, NULL, + flags, entry->key.addr, + entry->key.vlan_id); + hash_del(&entry->entry); + } else if (time_before(expires, next_timer)) { + next_timer = expires; + } + } + + spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags); + + mod_timer(&rocker->fdb_cleanup_timer, round_jiffies_up(next_timer)); +} + static int rocker_port_router_mac(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id) { u32 in_pport_mask = 0xffffffff; @@ -3733,7 +3765,7 @@ static int rocker_port_router_mac(struct rocker_port *rocker_port, } static int rocker_port_fwding(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { bool pop_vlan; u32 out_pport; @@ -3772,16 +3804,16 @@ static int rocker_port_fwding(struct rocker_port *rocker_port, } static int rocker_port_stp_update(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u8 state) { bool want[ROCKER_CTRL_MAX] = { 0, }; bool prev_ctrls[ROCKER_CTRL_MAX]; - u8 prev_state; + u8 uninitialized_var(prev_state); int err; int i; - if (trans == SWITCHDEV_TRANS_PREPARE) { + if (switchdev_trans_ph_prepare(trans)) { memcpy(prev_ctrls, rocker_port->ctrls, sizeof(prev_ctrls)); prev_state = rocker_port->stp_state; } @@ -3833,7 +3865,7 @@ static int rocker_port_stp_update(struct rocker_port *rocker_port, err = rocker_port_fwding(rocker_port, trans, flags); err_out: - if (trans == SWITCHDEV_TRANS_PREPARE) { + if (switchdev_trans_ph_prepare(trans)) { memcpy(rocker_port->ctrls, prev_ctrls, sizeof(prev_ctrls)); rocker_port->stp_state = prev_state; } @@ -3842,7 +3874,7 @@ err_out: } static int rocker_port_fwd_enable(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { if (rocker_port_is_bridged(rocker_port)) /* bridge STP will enable port */ @@ -3854,7 +3886,7 @@ static int rocker_port_fwd_enable(struct rocker_port *rocker_port, } static int rocker_port_fwd_disable(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { if (rocker_port_is_bridged(rocker_port)) /* bridge STP will disable port */ @@ -3952,7 +3984,7 @@ not_found: } static int rocker_port_fib_ipv4(struct rocker_port *rocker_port, - enum switchdev_trans trans, __be32 dst, + struct switchdev_trans *trans, __be32 dst, int dst_len, const struct fib_info *fi, u32 tb_id, int flags) { @@ -4026,7 +4058,7 @@ static int rocker_port_open(struct net_device *dev) goto err_request_rx_irq; } - err = rocker_port_fwd_enable(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_enable(rocker_port, NULL, 0); if (err) goto err_fwd_enable; @@ -4054,7 +4086,7 @@ static int rocker_port_stop(struct net_device *dev) rocker_port_set_enable(rocker_port, false); napi_disable(&rocker_port->napi_rx); napi_disable(&rocker_port->napi_tx); - rocker_port_fwd_disable(rocker_port, SWITCHDEV_TRANS_NONE, + rocker_port_fwd_disable(rocker_port, NULL, ROCKER_OP_FLAG_NOWAIT); free_irq(rocker_msix_rx_vector(rocker_port), rocker_port); free_irq(rocker_msix_tx_vector(rocker_port), rocker_port); @@ -4240,7 +4272,7 @@ static int rocker_port_get_phys_port_name(struct net_device *dev, struct port_name name = { .buf = buf, .len = len }; int err; - err = rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + err = rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_settings_prep, NULL, rocker_cmd_get_port_settings_phys_name_proc, &name); @@ -4265,7 +4297,7 @@ static void rocker_port_neigh_destroy(struct neighbour *n) int flags = ROCKER_OP_FLAG_REMOVE | ROCKER_OP_FLAG_NOWAIT; __be32 ip_addr = *(__be32 *)n->primary_key; - rocker_port_ipv4_neigh(rocker_port, SWITCHDEV_TRANS_NONE, + rocker_port_ipv4_neigh(rocker_port, NULL, flags, ip_addr, n->ha); } @@ -4297,11 +4329,11 @@ static int rocker_port_attr_get(struct net_device *dev, const struct rocker *rocker = rocker_port->rocker; switch (attr->id) { - case SWITCHDEV_ATTR_PORT_PARENT_ID: + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: attr->u.ppid.id_len = sizeof(rocker->hw.id); memcpy(&attr->u.ppid.id, &rocker->hw.id, attr->u.ppid.id_len); break; - case SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS: + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: attr->u.brport_flags = rocker_port->brport_flags; break; default: @@ -4311,18 +4343,8 @@ static int rocker_port_attr_get(struct net_device *dev, return 0; } -static void rocker_port_trans_abort(const struct rocker_port *rocker_port) -{ - struct list_head *mem, *tmp; - - list_for_each_safe(mem, tmp, &rocker_port->trans_mem) { - list_del(mem); - kfree(mem); - } -} - static int rocker_port_brport_flags_set(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, unsigned long brport_flags) { unsigned long orig_flags; @@ -4333,39 +4355,45 @@ static int rocker_port_brport_flags_set(struct rocker_port *rocker_port, if ((orig_flags ^ rocker_port->brport_flags) & BR_LEARNING) err = rocker_port_set_learning(rocker_port, trans); - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) rocker_port->brport_flags = orig_flags; return err; } +static int rocker_port_bridge_ageing_time(struct rocker_port *rocker_port, + struct switchdev_trans *trans, + u32 ageing_time) +{ + if (!switchdev_trans_ph_prepare(trans)) { + rocker_port->ageing_time = clock_t_to_jiffies(ageing_time); + mod_timer(&rocker_port->rocker->fdb_cleanup_timer, jiffies); + } + + return 0; +} + static int rocker_port_attr_set(struct net_device *dev, - struct switchdev_attr *attr) + struct switchdev_attr *attr, + struct switchdev_trans *trans) { struct rocker_port *rocker_port = netdev_priv(dev); int err = 0; - switch (attr->trans) { - case SWITCHDEV_TRANS_PREPARE: - BUG_ON(!list_empty(&rocker_port->trans_mem)); - break; - case SWITCHDEV_TRANS_ABORT: - rocker_port_trans_abort(rocker_port); - return 0; - default: - break; - } - switch (attr->id) { - case SWITCHDEV_ATTR_PORT_STP_STATE: - err = rocker_port_stp_update(rocker_port, attr->trans, + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: + err = rocker_port_stp_update(rocker_port, trans, ROCKER_OP_FLAG_NOWAIT, attr->u.stp_state); break; - case SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS: - err = rocker_port_brport_flags_set(rocker_port, attr->trans, + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: + err = rocker_port_brport_flags_set(rocker_port, trans, attr->u.brport_flags); break; + case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: + err = rocker_port_bridge_ageing_time(rocker_port, trans, + attr->u.ageing_time); + break; default: err = -EOPNOTSUPP; break; @@ -4375,7 +4403,8 @@ static int rocker_port_attr_set(struct net_device *dev, } static int rocker_port_vlan_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, u16 vid, u16 flags) + struct switchdev_trans *trans, + u16 vid, u16 flags) { int err; @@ -4394,8 +4423,8 @@ static int rocker_port_vlan_add(struct rocker_port *rocker_port, } static int rocker_port_vlans_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, - const struct switchdev_obj_vlan *vlan) + struct switchdev_trans *trans, + const struct switchdev_obj_port_vlan *vlan) { u16 vid; int err; @@ -4411,8 +4440,8 @@ static int rocker_port_vlans_add(struct rocker_port *rocker_port, } static int rocker_port_fdb_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, - const struct switchdev_obj_fdb *fdb) + struct switchdev_trans *trans, + const struct switchdev_obj_port_fdb *fdb) { __be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL); int flags = 0; @@ -4424,36 +4453,27 @@ static int rocker_port_fdb_add(struct rocker_port *rocker_port, } static int rocker_port_obj_add(struct net_device *dev, - struct switchdev_obj *obj) + const struct switchdev_obj *obj, + struct switchdev_trans *trans) { struct rocker_port *rocker_port = netdev_priv(dev); const struct switchdev_obj_ipv4_fib *fib4; int err = 0; - switch (obj->trans) { - case SWITCHDEV_TRANS_PREPARE: - BUG_ON(!list_empty(&rocker_port->trans_mem)); - break; - case SWITCHDEV_TRANS_ABORT: - rocker_port_trans_abort(rocker_port); - return 0; - default: - break; - } - switch (obj->id) { - case SWITCHDEV_OBJ_PORT_VLAN: - err = rocker_port_vlans_add(rocker_port, obj->trans, - &obj->u.vlan); + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = rocker_port_vlans_add(rocker_port, trans, + SWITCHDEV_OBJ_PORT_VLAN(obj)); break; - case SWITCHDEV_OBJ_IPV4_FIB: - fib4 = &obj->u.ipv4_fib; - err = rocker_port_fib_ipv4(rocker_port, obj->trans, + case SWITCHDEV_OBJ_ID_IPV4_FIB: + fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj); + err = rocker_port_fib_ipv4(rocker_port, trans, htonl(fib4->dst), fib4->dst_len, fib4->fi, fib4->tb_id, 0); break; - case SWITCHDEV_OBJ_PORT_FDB: - err = rocker_port_fdb_add(rocker_port, obj->trans, &obj->u.fdb); + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = rocker_port_fdb_add(rocker_port, trans, + SWITCHDEV_OBJ_PORT_FDB(obj)); break; default: err = -EOPNOTSUPP; @@ -4468,17 +4488,17 @@ static int rocker_port_vlan_del(struct rocker_port *rocker_port, { int err; - err = rocker_port_router_mac(rocker_port, SWITCHDEV_TRANS_NONE, + err = rocker_port_router_mac(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE, htons(vid)); if (err) return err; - return rocker_port_vlan(rocker_port, SWITCHDEV_TRANS_NONE, + return rocker_port_vlan(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE, vid); } static int rocker_port_vlans_del(struct rocker_port *rocker_port, - const struct switchdev_obj_vlan *vlan) + const struct switchdev_obj_port_vlan *vlan) { u16 vid; int err; @@ -4493,8 +4513,8 @@ static int rocker_port_vlans_del(struct rocker_port *rocker_port, } static int rocker_port_fdb_del(struct rocker_port *rocker_port, - enum switchdev_trans trans, - const struct switchdev_obj_fdb *fdb) + struct switchdev_trans *trans, + const struct switchdev_obj_port_fdb *fdb) { __be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL); int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE; @@ -4506,25 +4526,27 @@ static int rocker_port_fdb_del(struct rocker_port *rocker_port, } static int rocker_port_obj_del(struct net_device *dev, - struct switchdev_obj *obj) + const struct switchdev_obj *obj) { struct rocker_port *rocker_port = netdev_priv(dev); const struct switchdev_obj_ipv4_fib *fib4; int err = 0; switch (obj->id) { - case SWITCHDEV_OBJ_PORT_VLAN: - err = rocker_port_vlans_del(rocker_port, &obj->u.vlan); + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = rocker_port_vlans_del(rocker_port, + SWITCHDEV_OBJ_PORT_VLAN(obj)); break; - case SWITCHDEV_OBJ_IPV4_FIB: - fib4 = &obj->u.ipv4_fib; - err = rocker_port_fib_ipv4(rocker_port, SWITCHDEV_TRANS_NONE, + case SWITCHDEV_OBJ_ID_IPV4_FIB: + fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj); + err = rocker_port_fib_ipv4(rocker_port, NULL, htonl(fib4->dst), fib4->dst_len, fib4->fi, fib4->tb_id, ROCKER_OP_FLAG_REMOVE); break; - case SWITCHDEV_OBJ_PORT_FDB: - err = rocker_port_fdb_del(rocker_port, obj->trans, &obj->u.fdb); + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = rocker_port_fdb_del(rocker_port, NULL, + SWITCHDEV_OBJ_PORT_FDB(obj)); break; default: err = -EOPNOTSUPP; @@ -4535,10 +4557,10 @@ static int rocker_port_obj_del(struct net_device *dev, } static int rocker_port_fdb_dump(const struct rocker_port *rocker_port, - struct switchdev_obj *obj) + struct switchdev_obj_port_fdb *fdb, + switchdev_obj_dump_cb_t *cb) { struct rocker *rocker = rocker_port->rocker; - struct switchdev_obj_fdb *fdb = &obj->u.fdb; struct rocker_fdb_tbl_entry *found; struct hlist_node *tmp; unsigned long lock_flags; @@ -4547,13 +4569,13 @@ static int rocker_port_fdb_dump(const struct rocker_port *rocker_port, spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags); hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) { - if (found->key.pport != rocker_port->pport) + if (found->key.rocker_port != rocker_port) continue; fdb->addr = found->key.addr; fdb->ndm_state = NUD_REACHABLE; fdb->vid = rocker_port_vlan_to_vid(rocker_port, found->key.vlan_id); - err = obj->cb(rocker_port->dev, obj); + err = cb(&fdb->obj); if (err) break; } @@ -4563,9 +4585,9 @@ static int rocker_port_fdb_dump(const struct rocker_port *rocker_port, } static int rocker_port_vlan_dump(const struct rocker_port *rocker_port, - struct switchdev_obj *obj) + struct switchdev_obj_port_vlan *vlan, + switchdev_obj_dump_cb_t *cb) { - struct switchdev_obj_vlan *vlan = &obj->u.vlan; u16 vid; int err = 0; @@ -4576,7 +4598,7 @@ static int rocker_port_vlan_dump(const struct rocker_port *rocker_port, if (rocker_vlan_id_is_internal(htons(vid))) vlan->flags |= BRIDGE_VLAN_INFO_PVID; vlan->vid_begin = vlan->vid_end = vid; - err = obj->cb(rocker_port->dev, obj); + err = cb(&vlan->obj); if (err) break; } @@ -4585,17 +4607,20 @@ static int rocker_port_vlan_dump(const struct rocker_port *rocker_port, } static int rocker_port_obj_dump(struct net_device *dev, - struct switchdev_obj *obj) + struct switchdev_obj *obj, + switchdev_obj_dump_cb_t *cb) { const struct rocker_port *rocker_port = netdev_priv(dev); int err = 0; switch (obj->id) { - case SWITCHDEV_OBJ_PORT_FDB: - err = rocker_port_fdb_dump(rocker_port, obj); + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = rocker_port_fdb_dump(rocker_port, + SWITCHDEV_OBJ_PORT_FDB(obj), cb); break; - case SWITCHDEV_OBJ_PORT_VLAN: - err = rocker_port_vlan_dump(rocker_port, obj); + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = rocker_port_vlan_dump(rocker_port, + SWITCHDEV_OBJ_PORT_VLAN(obj), cb); break; default: err = -EOPNOTSUPP; @@ -4738,7 +4763,7 @@ rocker_cmd_get_port_stats_ethtool_proc(const struct rocker_port *rocker_port, static int rocker_cmd_get_port_stats_ethtool(struct rocker_port *rocker_port, void *priv) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_stats_prep, NULL, rocker_cmd_get_port_stats_ethtool_proc, priv); @@ -4930,8 +4955,7 @@ static void rocker_remove_ports(const struct rocker *rocker) rocker_port = rocker->ports[i]; if (!rocker_port) continue; - rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE, - ROCKER_OP_FLAG_REMOVE); + rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE); unregister_netdev(rocker_port->dev); free_netdev(rocker_port->dev); } @@ -4969,7 +4993,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) rocker_port->port_number = port_number; rocker_port->pport = port_number + 1; rocker_port->brport_flags = BR_LEARNING | BR_LEARNING_SYNC; - INIT_LIST_HEAD(&rocker_port->trans_mem); + rocker_port->ageing_time = BR_DEFAULT_AGEING_TIME; rocker_port_dev_addr_init(rocker_port); dev->netdev_ops = &rocker_port_netdev_ops; @@ -4992,9 +5016,9 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) switchdev_port_fwd_mark_set(rocker_port->dev, NULL, false); - rocker_port_set_learning(rocker_port, SWITCHDEV_TRANS_NONE); + rocker_port_set_learning(rocker_port, NULL); - err = rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_ig_tbl(rocker_port, NULL, 0); if (err) { netdev_err(rocker_port->dev, "install ig port table failed\n"); goto err_port_ig_tbl; @@ -5003,8 +5027,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) rocker_port->internal_vlan_id = rocker_port_internal_vlan_id_get(rocker_port, dev->ifindex); - err = rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE, - untagged_vid, 0); + err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); if (err) { netdev_err(rocker_port->dev, "install untagged VLAN failed\n"); goto err_untagged_vlan; @@ -5013,8 +5036,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) return 0; err_untagged_vlan: - rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE, - ROCKER_OP_FLAG_REMOVE); + rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE); err_port_ig_tbl: rocker->ports[port_number] = NULL; unregister_netdev(dev); @@ -5183,6 +5205,10 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_init_tbls; } + setup_timer(&rocker->fdb_cleanup_timer, rocker_fdb_cleanup, + (unsigned long) rocker); + mod_timer(&rocker->fdb_cleanup_timer, jiffies); + err = rocker_probe_ports(rocker); if (err) { dev_err(&pdev->dev, "failed to probe ports\n"); @@ -5195,6 +5221,7 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err_probe_ports: + del_timer_sync(&rocker->fdb_cleanup_timer); rocker_free_tbls(rocker); err_init_tbls: free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker); @@ -5222,6 +5249,7 @@ static void rocker_remove(struct pci_dev *pdev) { struct rocker *rocker = pci_get_drvdata(pdev); + del_timer_sync(&rocker->fdb_cleanup_timer); rocker_free_tbls(rocker); rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET); rocker_remove_ports(rocker); @@ -5275,8 +5303,7 @@ static int rocker_port_bridge_join(struct rocker_port *rocker_port, rocker_port->bridge_dev = bridge; switchdev_port_fwd_mark_set(rocker_port->dev, bridge, true); - return rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE, - untagged_vid, 0); + return rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); } static int rocker_port_bridge_leave(struct rocker_port *rocker_port) @@ -5298,14 +5325,12 @@ static int rocker_port_bridge_leave(struct rocker_port *rocker_port) false); rocker_port->bridge_dev = NULL; - err = rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE, - untagged_vid, 0); + err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); if (err) return err; if (rocker_port->dev->flags & IFF_UP) - err = rocker_port_fwd_enable(rocker_port, - SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_enable(rocker_port, NULL, 0); return err; } @@ -5318,10 +5343,10 @@ static int rocker_port_ovs_changed(struct rocker_port *rocker_port, rocker_port->bridge_dev = master; - err = rocker_port_fwd_disable(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_disable(rocker_port, NULL, 0); if (err) return err; - err = rocker_port_fwd_enable(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_enable(rocker_port, NULL, 0); return err; } @@ -5399,8 +5424,7 @@ static int rocker_neigh_update(struct net_device *dev, struct neighbour *n) ROCKER_OP_FLAG_NOWAIT; __be32 ip_addr = *(__be32 *)n->primary_key; - return rocker_port_ipv4_neigh(rocker_port, SWITCHDEV_TRANS_NONE, - flags, ip_addr, n->ha); + return rocker_port_ipv4_neigh(rocker_port, NULL, flags, ip_addr, n->ha); } static int rocker_netevent_event(struct notifier_block *unused, diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index ff649ebef637..78b7b7bcae37 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -1604,6 +1604,22 @@ efx_ef10_mcdi_read_response(struct efx_nic *efx, efx_dword_t *outbuf, memcpy(outbuf, pdu + offset, outlen); } +static void efx_ef10_mcdi_reboot_detected(struct efx_nic *efx) +{ + struct efx_ef10_nic_data *nic_data = efx->nic_data; + + /* All our allocations have been reset */ + efx_ef10_reset_mc_allocations(efx); + + /* The datapath firmware might have been changed */ + nic_data->must_check_datapath_caps = true; + + /* MAC statistics have been cleared on the NIC; clear the local + * statistic that we update with efx_update_diff_stat(). + */ + nic_data->stats[EF10_STAT_port_rx_bad_bytes] = 0; +} + static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx) { struct efx_ef10_nic_data *nic_data = efx->nic_data; @@ -1623,17 +1639,7 @@ static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx) return 0; nic_data->warm_boot_count = rc; - - /* All our allocations have been reset */ - efx_ef10_reset_mc_allocations(efx); - - /* The datapath firmware might have been changed */ - nic_data->must_check_datapath_caps = true; - - /* MAC statistics have been cleared on the NIC; clear the local - * statistic that we update with efx_update_diff_stat(). - */ - nic_data->stats[EF10_STAT_port_rx_bad_bytes] = 0; + efx_ef10_mcdi_reboot_detected(efx); return -EIO; } @@ -4670,6 +4676,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = { .mcdi_poll_response = efx_ef10_mcdi_poll_response, .mcdi_read_response = efx_ef10_mcdi_read_response, .mcdi_poll_reboot = efx_ef10_mcdi_poll_reboot, + .mcdi_reboot_detected = efx_ef10_mcdi_reboot_detected, .irq_enable_master = efx_port_dummy_op_void, .irq_test_generate = efx_ef10_irq_test_generate, .irq_disable_non_ev = efx_port_dummy_op_void, @@ -4774,6 +4781,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .mcdi_poll_response = efx_ef10_mcdi_poll_response, .mcdi_read_response = efx_ef10_mcdi_read_response, .mcdi_poll_reboot = efx_ef10_mcdi_poll_reboot, + .mcdi_reboot_detected = efx_ef10_mcdi_reboot_detected, .irq_enable_master = efx_port_dummy_op_void, .irq_test_generate = efx_ef10_irq_test_generate, .irq_disable_non_ev = efx_port_dummy_op_void, diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index 98d172b04f71..d3f307e5c070 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -1028,10 +1028,21 @@ static void efx_mcdi_ev_death(struct efx_nic *efx, int rc) /* Consume the status word since efx_mcdi_rpc_finish() won't */ for (count = 0; count < MCDI_STATUS_DELAY_COUNT; ++count) { - if (efx_mcdi_poll_reboot(efx)) + rc = efx_mcdi_poll_reboot(efx); + if (rc) break; udelay(MCDI_STATUS_DELAY_US); } + + /* On EF10, a CODE_MC_REBOOT event can be received without the + * reboot detection in efx_mcdi_poll_reboot() being triggered. + * If zero was returned from the final call to + * efx_mcdi_poll_reboot(), the MC reboot wasn't noticed but the + * MC has definitely rebooted so prepare for the reset. + */ + if (!rc && efx->type->mcdi_reboot_detected) + efx->type->mcdi_reboot_detected(efx); + mcdi->new_epoch = true; /* Nobody was waiting for an MCDI request, so trigger a reset */ diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index c530e1c4cb4f..ad56231743a6 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -1277,6 +1277,7 @@ struct efx_nic_type { void (*mcdi_read_response)(struct efx_nic *efx, efx_dword_t *pdu, size_t pdu_offset, size_t pdu_len); int (*mcdi_poll_reboot)(struct efx_nic *efx); + void (*mcdi_reboot_detected)(struct efx_nic *efx); void (*irq_enable_master)(struct efx_nic *efx); void (*irq_test_generate)(struct efx_nic *efx); void (*irq_disable_non_ev)(struct efx_nic *efx); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 925f2f8659b8..64d8aa4e0cad 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -424,7 +424,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) { struct stmmac_priv *priv = netdev_priv(dev); struct hwtstamp_config config; - struct timespec now; + struct timespec64 now; u64 temp = 0; u32 ptp_v2 = 0; u32 tstamp_all = 0; @@ -621,8 +621,10 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) priv->default_addend); /* initialize system time */ - getnstimeofday(&now); - priv->hw->ptp->init_systime(priv->ioaddr, now.tv_sec, + ktime_get_real_ts64(&now); + + /* lower 32 bits of tv_sec are safe until y2106 */ + priv->hw->ptp->init_systime(priv->ioaddr, (u32)now.tv_sec, now.tv_nsec); } @@ -1945,7 +1947,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); unsigned int txsize = priv->dma_tx_size; - unsigned int entry; + int entry; int i, csum_insertion = 0, is_jumbo = 0; int nfrags = skb_shinfo(skb)->nr_frags; struct dma_desc *desc, *first; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index b735fa22ac95..ebf6abc4853f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -161,11 +161,16 @@ int stmmac_mdio_reset(struct mii_bus *bus) if (!gpio_request(reset_gpio, "mdio-reset")) { gpio_direction_output(reset_gpio, active_low ? 1 : 0); - udelay(data->delays[0]); + if (data->delays[0]) + msleep(DIV_ROUND_UP(data->delays[0], 1000)); + gpio_set_value(reset_gpio, active_low ? 0 : 1); - udelay(data->delays[1]); + if (data->delays[1]) + msleep(DIV_ROUND_UP(data->delays[1], 1000)); + gpio_set_value(reset_gpio, active_low ? 1 : 0); - udelay(data->delays[2]); + if (data->delays[2]) + msleep(DIV_ROUND_UP(data->delays[2], 1000)); } } #endif diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 53fe200e0b79..cc106d892e29 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -1756,7 +1756,8 @@ static const struct net_device_ops vnet_ops = { #endif }; -static struct vnet *vnet_new(const u64 *local_mac) +static struct vnet *vnet_new(const u64 *local_mac, + struct vio_dev *vdev) { struct net_device *dev; struct vnet *vp; @@ -1790,6 +1791,8 @@ static struct vnet *vnet_new(const u64 *local_mac) NETIF_F_HW_CSUM | NETIF_F_SG; dev->features = dev->hw_features; + SET_NETDEV_DEV(dev, &vdev->dev); + err = register_netdev(dev); if (err) { pr_err("Cannot register net device, aborting\n"); @@ -1808,7 +1811,8 @@ err_out_free_dev: return ERR_PTR(err); } -static struct vnet *vnet_find_or_create(const u64 *local_mac) +static struct vnet *vnet_find_or_create(const u64 *local_mac, + struct vio_dev *vdev) { struct vnet *iter, *vp; @@ -1821,7 +1825,7 @@ static struct vnet *vnet_find_or_create(const u64 *local_mac) } } if (!vp) - vp = vnet_new(local_mac); + vp = vnet_new(local_mac, vdev); mutex_unlock(&vnet_list_mutex); return vp; @@ -1848,7 +1852,8 @@ static void vnet_cleanup(void) static const char *local_mac_prop = "local-mac-address"; static struct vnet *vnet_find_parent(struct mdesc_handle *hp, - u64 port_node) + u64 port_node, + struct vio_dev *vdev) { const u64 *local_mac = NULL; u64 a; @@ -1869,7 +1874,7 @@ static struct vnet *vnet_find_parent(struct mdesc_handle *hp, if (!local_mac) return ERR_PTR(-ENODEV); - return vnet_find_or_create(local_mac); + return vnet_find_or_create(local_mac, vdev); } static struct ldc_channel_config vnet_ldc_cfg = { @@ -1923,7 +1928,7 @@ static int vnet_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) hp = mdesc_grab(); - vp = vnet_find_parent(hp, vdev->mp); + vp = vnet_find_parent(hp, vdev->mp, vdev); if (IS_ERR(vp)) { pr_err("Cannot find port parent vnet\n"); err = PTR_ERR(vp); diff --git a/drivers/net/ethernet/ti/cpsw-common.c b/drivers/net/ethernet/ti/cpsw-common.c index f59509486113..c08be62bceba 100644 --- a/drivers/net/ethernet/ti/cpsw-common.c +++ b/drivers/net/ethernet/ti/cpsw-common.c @@ -19,11 +19,38 @@ #include "cpsw.h" -#define AM33XX_CTRL_MAC_LO_REG(offset, id) ((offset) + 0x8 * (id)) -#define AM33XX_CTRL_MAC_HI_REG(offset, id) ((offset) + 0x8 * (id) + 0x4) +#define CTRL_MAC_LO_REG(offset, id) ((offset) + 0x8 * (id)) +#define CTRL_MAC_HI_REG(offset, id) ((offset) + 0x8 * (id) + 0x4) -int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, - u8 *mac_addr) +static int davinci_emac_3517_get_macid(struct device *dev, u16 offset, + int slave, u8 *mac_addr) +{ + u32 macid_lsb; + u32 macid_msb; + struct regmap *syscon; + + syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); + if (IS_ERR(syscon)) { + if (PTR_ERR(syscon) == -ENODEV) + return 0; + return PTR_ERR(syscon); + } + + regmap_read(syscon, CTRL_MAC_LO_REG(offset, slave), &macid_lsb); + regmap_read(syscon, CTRL_MAC_HI_REG(offset, slave), &macid_msb); + + mac_addr[0] = (macid_msb >> 16) & 0xff; + mac_addr[1] = (macid_msb >> 8) & 0xff; + mac_addr[2] = macid_msb & 0xff; + mac_addr[3] = (macid_lsb >> 16) & 0xff; + mac_addr[4] = (macid_lsb >> 8) & 0xff; + mac_addr[5] = macid_lsb & 0xff; + + return 0; +} + +static int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, + u8 *mac_addr) { u32 macid_lo; u32 macid_hi; @@ -36,10 +63,8 @@ int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, return PTR_ERR(syscon); } - regmap_read(syscon, AM33XX_CTRL_MAC_LO_REG(offset, slave), - &macid_lo); - regmap_read(syscon, AM33XX_CTRL_MAC_HI_REG(offset, slave), - &macid_hi); + regmap_read(syscon, CTRL_MAC_LO_REG(offset, slave), &macid_lo); + regmap_read(syscon, CTRL_MAC_HI_REG(offset, slave), &macid_hi); mac_addr[5] = (macid_lo >> 8) & 0xff; mac_addr[4] = macid_lo & 0xff; @@ -50,6 +75,27 @@ int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, return 0; } -EXPORT_SYMBOL_GPL(cpsw_am33xx_cm_get_macid); + +int ti_cm_get_macid(struct device *dev, int slave, u8 *mac_addr) +{ + if (of_machine_is_compatible("ti,am33xx")) + return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr); + + if (of_device_is_compatible(dev->of_node, "ti,am3517-emac")) + return davinci_emac_3517_get_macid(dev, 0x110, slave, mac_addr); + + if (of_device_is_compatible(dev->of_node, "ti,dm816-emac")) + return cpsw_am33xx_cm_get_macid(dev, 0x30, slave, mac_addr); + + if (of_machine_is_compatible("ti,am4372")) + return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr); + + if (of_machine_is_compatible("ti,dra7")) + return davinci_emac_3517_get_macid(dev, 0x514, slave, mac_addr); + + dev_err(dev, "incompatible machine/device type for reading mac address\n"); + return -ENOENT; +} +EXPORT_SYMBOL_GPL(ti_cm_get_macid); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/ti/cpsw-phy-sel.c b/drivers/net/ethernet/ti/cpsw-phy-sel.c index 0ea78326cc21..e9cc61e1ec74 100644 --- a/drivers/net/ethernet/ti/cpsw-phy-sel.c +++ b/drivers/net/ethernet/ti/cpsw-phy-sel.c @@ -2,6 +2,8 @@ * * Copyright (C) 2013 Texas Instruments * + * Module Author: Mugunthan V N <mugunthanvnm@ti.com> + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. @@ -13,7 +15,7 @@ */ #include <linux/platform_device.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/netdevice.h> #include <linux/phy.h> #include <linux/of.h> @@ -173,7 +175,6 @@ static const struct of_device_id cpsw_phy_sel_id_table[] = { }, {} }; -MODULE_DEVICE_TABLE(of, cpsw_phy_sel_id_table); static int cpsw_phy_sel_probe(struct platform_device *pdev) { @@ -214,7 +215,4 @@ static struct platform_driver cpsw_phy_sel_driver = { .of_match_table = cpsw_phy_sel_id_table, }, }; - -module_platform_driver(cpsw_phy_sel_driver); -MODULE_AUTHOR("Mugunthan V N <mugunthanvnm@ti.com>"); -MODULE_LICENSE("GPL v2"); +builtin_platform_driver(cpsw_phy_sel_driver); diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index c670317f3cb0..75584cc36339 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2058,13 +2058,10 @@ no_phy_slave: if (mac_addr) { memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN); } else { - if (of_machine_is_compatible("ti,am33xx")) { - ret = cpsw_am33xx_cm_get_macid(&pdev->dev, - 0x630, i, - slave_data->mac_addr); - if (ret) - return ret; - } + ret = ti_cm_get_macid(&pdev->dev, i, + slave_data->mac_addr); + if (ret) + return ret; } if (data->dual_emac) { if (of_property_read_u32(slave_node, "dual_emac_res_vlan", diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h index ca90efafd156..442a7038e660 100644 --- a/drivers/net/ethernet/ti/cpsw.h +++ b/drivers/net/ethernet/ti/cpsw.h @@ -41,7 +41,6 @@ struct cpsw_platform_data { }; void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave); -int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, - u8 *mac_addr); +int ti_cm_get_macid(struct device *dev, int slave, u8 *mac_addr); #endif /* __CPSW_H__ */ diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index a21c77bc1b27..33bd3b902304 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1861,8 +1861,12 @@ davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv) pdata->no_bd_ram = of_property_read_bool(np, "ti,davinci-no-bd-ram"); priv->phy_node = of_parse_phandle(np, "phy-handle", 0); - if (!priv->phy_node) - pdata->phy_id = NULL; + if (!priv->phy_node) { + if (!of_phy_is_fixed_link(np)) + pdata->phy_id = NULL; + else if (of_phy_register_fixed_link(np) >= 0) + priv->phy_node = of_node_get(np); + } auxdata = pdev->dev.platform_data; if (auxdata) { @@ -1882,51 +1886,13 @@ davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv) return pdata; } -static int davinci_emac_3517_get_macid(struct device *dev, u16 offset, - int slave, u8 *mac_addr) -{ - u32 macid_lsb; - u32 macid_msb; - struct regmap *syscon; - - syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); - if (IS_ERR(syscon)) { - if (PTR_ERR(syscon) == -ENODEV) - return 0; - return PTR_ERR(syscon); - } - - regmap_read(syscon, offset, &macid_lsb); - regmap_read(syscon, offset + 4, &macid_msb); - - mac_addr[0] = (macid_msb >> 16) & 0xff; - mac_addr[1] = (macid_msb >> 8) & 0xff; - mac_addr[2] = macid_msb & 0xff; - mac_addr[3] = (macid_lsb >> 16) & 0xff; - mac_addr[4] = (macid_lsb >> 8) & 0xff; - mac_addr[5] = macid_lsb & 0xff; - - return 0; -} - static int davinci_emac_try_get_mac(struct platform_device *pdev, int instance, u8 *mac_addr) { - int error = -EINVAL; - if (!pdev->dev.of_node) - return error; - - if (of_device_is_compatible(pdev->dev.of_node, "ti,am3517-emac")) - error = davinci_emac_3517_get_macid(&pdev->dev, 0x110, - 0, mac_addr); - else if (of_device_is_compatible(pdev->dev.of_node, - "ti,dm816-emac")) - error = cpsw_am33xx_cm_get_macid(&pdev->dev, 0x30, - instance, - mac_addr); - - return error; + return -EINVAL; + + return ti_cm_get_macid(&pdev->dev, instance, mac_addr); } /** diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 1a5aca55ea9f..9f9832f0dea9 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -291,13 +291,6 @@ static int netcp_module_probe(struct netcp_device *netcp_device, interface_list) { struct netcp_intf_modpriv *intf_modpriv; - /* If interface not registered then register now */ - if (!netcp_intf->netdev_registered) - ret = netcp_register_interface(netcp_intf); - - if (ret) - return -ENODEV; - intf_modpriv = devm_kzalloc(dev, sizeof(*intf_modpriv), GFP_KERNEL); if (!intf_modpriv) @@ -306,6 +299,11 @@ static int netcp_module_probe(struct netcp_device *netcp_device, interface = of_parse_phandle(netcp_intf->node_interface, module->name, 0); + if (!interface) { + devm_kfree(dev, intf_modpriv); + continue; + } + intf_modpriv->netcp_priv = netcp_intf; intf_modpriv->netcp_module = module; list_add_tail(&intf_modpriv->intf_list, @@ -323,6 +321,18 @@ static int netcp_module_probe(struct netcp_device *netcp_device, continue; } } + + /* Now register the interface with netdev */ + list_for_each_entry(netcp_intf, + &netcp_device->interface_head, + interface_list) { + /* If interface not registered then register now */ + if (!netcp_intf->netdev_registered) { + ret = netcp_register_interface(netcp_intf); + if (ret) + return -ENODEV; + } + } return 0; } @@ -357,7 +367,6 @@ int netcp_register_module(struct netcp_module *module) if (ret < 0) goto fail; } - mutex_unlock(&netcp_modules_lock); return 0; @@ -796,7 +805,7 @@ static void netcp_rxpool_free(struct netcp_intf *netcp) netcp->rx_pool = NULL; } -static void netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq) +static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq) { struct knav_dma_desc *hwdesc; unsigned int buf_len, dma_sz; @@ -810,7 +819,7 @@ static void netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq) hwdesc = knav_pool_desc_get(netcp->rx_pool); if (IS_ERR_OR_NULL(hwdesc)) { dev_dbg(netcp->ndev_dev, "out of rx pool desc\n"); - return; + return -ENOMEM; } if (likely(fdq == 0)) { @@ -862,25 +871,26 @@ static void netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq) knav_pool_desc_map(netcp->rx_pool, hwdesc, sizeof(*hwdesc), &dma, &dma_sz); knav_queue_push(netcp->rx_fdq[fdq], dma, sizeof(*hwdesc), 0); - return; + return 0; fail: knav_pool_desc_put(netcp->rx_pool, hwdesc); + return -ENOMEM; } /* Refill Rx FDQ with descriptors & attached buffers */ static void netcp_rxpool_refill(struct netcp_intf *netcp) { u32 fdq_deficit[KNAV_DMA_FDQ_PER_CHAN] = {0}; - int i; + int i, ret = 0; /* Calculate the FDQ deficit and refill */ for (i = 0; i < KNAV_DMA_FDQ_PER_CHAN && netcp->rx_fdq[i]; i++) { fdq_deficit[i] = netcp->rx_queue_depths[i] - knav_queue_get_count(netcp->rx_fdq[i]); - while (fdq_deficit[i]--) - netcp_allocate_rx_buf(netcp, i); + while (fdq_deficit[i]-- && !ret) + ret = netcp_allocate_rx_buf(netcp, i); } /* end for fdqs */ } @@ -893,12 +903,12 @@ static int netcp_rx_poll(struct napi_struct *napi, int budget) packets = netcp_process_rx_packets(netcp, budget); + netcp_rxpool_refill(netcp); if (packets < budget) { napi_complete(&netcp->rx_napi); knav_queue_enable_notify(netcp->rx_queue); } - netcp_rxpool_refill(netcp); return packets; } @@ -1384,7 +1394,6 @@ static void netcp_addr_sweep_del(struct netcp_intf *netcp) continue; dev_dbg(netcp->ndev_dev, "deleting address %pM, type %x\n", naddr->addr, naddr->type); - mutex_lock(&netcp_modules_lock); for_each_module(netcp, priv) { module = priv->netcp_module; if (!module->del_addr) @@ -1393,7 +1402,6 @@ static void netcp_addr_sweep_del(struct netcp_intf *netcp) naddr); WARN_ON(error); } - mutex_unlock(&netcp_modules_lock); netcp_addr_del(netcp, naddr); } } @@ -1410,7 +1418,7 @@ static void netcp_addr_sweep_add(struct netcp_intf *netcp) continue; dev_dbg(netcp->ndev_dev, "adding address %pM, type %x\n", naddr->addr, naddr->type); - mutex_lock(&netcp_modules_lock); + for_each_module(netcp, priv) { module = priv->netcp_module; if (!module->add_addr) @@ -1418,7 +1426,6 @@ static void netcp_addr_sweep_add(struct netcp_intf *netcp) error = module->add_addr(priv->module_priv, naddr); WARN_ON(error); } - mutex_unlock(&netcp_modules_lock); } } @@ -1432,6 +1439,7 @@ static void netcp_set_rx_mode(struct net_device *ndev) ndev->flags & IFF_ALLMULTI || netdev_mc_count(ndev) > NETCP_MAX_MCAST_ADDR); + spin_lock(&netcp->lock); /* first clear all marks */ netcp_addr_clear_mark(netcp); @@ -1450,6 +1458,7 @@ static void netcp_set_rx_mode(struct net_device *ndev) /* finally sweep and callout into modules */ netcp_addr_sweep_del(netcp); netcp_addr_sweep_add(netcp); + spin_unlock(&netcp->lock); } static void netcp_free_navigator_resources(struct netcp_intf *netcp) @@ -1614,7 +1623,6 @@ static int netcp_ndo_open(struct net_device *ndev) goto fail; } - mutex_lock(&netcp_modules_lock); for_each_module(netcp, intf_modpriv) { module = intf_modpriv->netcp_module; if (module->open) { @@ -1625,7 +1633,6 @@ static int netcp_ndo_open(struct net_device *ndev) } } } - mutex_unlock(&netcp_modules_lock); napi_enable(&netcp->rx_napi); napi_enable(&netcp->tx_napi); @@ -1642,7 +1649,6 @@ fail_open: if (module->close) module->close(intf_modpriv->module_priv, ndev); } - mutex_unlock(&netcp_modules_lock); fail: netcp_free_navigator_resources(netcp); @@ -1666,7 +1672,6 @@ static int netcp_ndo_stop(struct net_device *ndev) napi_disable(&netcp->rx_napi); napi_disable(&netcp->tx_napi); - mutex_lock(&netcp_modules_lock); for_each_module(netcp, intf_modpriv) { module = intf_modpriv->netcp_module; if (module->close) { @@ -1675,7 +1680,6 @@ static int netcp_ndo_stop(struct net_device *ndev) dev_err(netcp->ndev_dev, "Close failed\n"); } } - mutex_unlock(&netcp_modules_lock); /* Recycle Rx descriptors from completion queue */ netcp_empty_rx_queue(netcp); @@ -1703,7 +1707,6 @@ static int netcp_ndo_ioctl(struct net_device *ndev, if (!netif_running(ndev)) return -EINVAL; - mutex_lock(&netcp_modules_lock); for_each_module(netcp, intf_modpriv) { module = intf_modpriv->netcp_module; if (!module->ioctl) @@ -1719,7 +1722,6 @@ static int netcp_ndo_ioctl(struct net_device *ndev, } out: - mutex_unlock(&netcp_modules_lock); return (ret == 0) ? 0 : err; } @@ -1754,11 +1756,12 @@ static int netcp_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid) struct netcp_intf *netcp = netdev_priv(ndev); struct netcp_intf_modpriv *intf_modpriv; struct netcp_module *module; + unsigned long flags; int err = 0; dev_dbg(netcp->ndev_dev, "adding rx vlan id: %d\n", vid); - mutex_lock(&netcp_modules_lock); + spin_lock_irqsave(&netcp->lock, flags); for_each_module(netcp, intf_modpriv) { module = intf_modpriv->netcp_module; if ((module->add_vid) && (vid != 0)) { @@ -1770,7 +1773,8 @@ static int netcp_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid) } } } - mutex_unlock(&netcp_modules_lock); + spin_unlock_irqrestore(&netcp->lock, flags); + return err; } @@ -1779,11 +1783,12 @@ static int netcp_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid) struct netcp_intf *netcp = netdev_priv(ndev); struct netcp_intf_modpriv *intf_modpriv; struct netcp_module *module; + unsigned long flags; int err = 0; dev_dbg(netcp->ndev_dev, "removing rx vlan id: %d\n", vid); - mutex_lock(&netcp_modules_lock); + spin_lock_irqsave(&netcp->lock, flags); for_each_module(netcp, intf_modpriv) { module = intf_modpriv->netcp_module; if (module->del_vid) { @@ -1795,7 +1800,7 @@ static int netcp_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid) } } } - mutex_unlock(&netcp_modules_lock); + spin_unlock_irqrestore(&netcp->lock, flags); return err; } @@ -2040,7 +2045,6 @@ static int netcp_probe(struct platform_device *pdev) struct device_node *child, *interfaces; struct netcp_device *netcp_device; struct device *dev = &pdev->dev; - struct netcp_module *module; int ret; if (!node) { @@ -2087,14 +2091,6 @@ static int netcp_probe(struct platform_device *pdev) /* Add the device instance to the list */ list_add_tail(&netcp_device->device_list, &netcp_devices); - /* Probe & attach any modules already registered */ - mutex_lock(&netcp_modules_lock); - for_each_netcp_module(module) { - ret = netcp_module_probe(netcp_device, module); - if (ret < 0) - dev_err(dev, "module(%s) probe failed\n", module->name); - } - mutex_unlock(&netcp_modules_lock); return 0; probe_quit_interface: diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c index 6f16d6aaf7b7..6bff8d82ceab 100644 --- a/drivers/net/ethernet/ti/netcp_ethss.c +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -77,6 +77,7 @@ #define GBENU_ALE_OFFSET 0x1e000 #define GBENU_HOST_PORT_NUM 0 #define GBENU_NUM_ALE_ENTRIES 1024 +#define GBENU_SGMII_MODULE_SIZE 0x100 /* 10G Ethernet SS defines */ #define XGBE_MODULE_NAME "netcp-xgbe" @@ -149,8 +150,8 @@ #define XGBE_STATS2_MODULE 2 /* s: 0-based slave_port */ -#define SGMII_BASE(s) \ - (((s) < 2) ? gbe_dev->sgmii_port_regs : gbe_dev->sgmii_port34_regs) +#define SGMII_BASE(d, s) \ + (((s) < 2) ? (d)->sgmii_port_regs : (d)->sgmii_port34_regs) #define GBE_TX_QUEUE 648 #define GBE_TXHOOK_ORDER 0 @@ -1997,13 +1998,8 @@ static void netcp_ethss_update_link_state(struct gbe_priv *gbe_dev, return; if (!SLAVE_LINK_IS_XGMII(slave)) { - if (gbe_dev->ss_version == GBE_SS_VERSION_14) - sgmii_link_state = - netcp_sgmii_get_port_link(SGMII_BASE(sp), sp); - else - sgmii_link_state = - netcp_sgmii_get_port_link( - gbe_dev->sgmii_port_regs, sp); + sgmii_link_state = + netcp_sgmii_get_port_link(SGMII_BASE(gbe_dev, sp), sp); } phy_link_state = gbe_phy_link_status(slave); @@ -2100,17 +2096,11 @@ static void gbe_port_config(struct gbe_priv *gbe_dev, struct gbe_slave *slave, static void gbe_sgmii_rtreset(struct gbe_priv *priv, struct gbe_slave *slave, bool set) { - void __iomem *sgmii_port_regs; - if (SLAVE_LINK_IS_XGMII(slave)) return; - if ((priv->ss_version == GBE_SS_VERSION_14) && (slave->slave_num >= 2)) - sgmii_port_regs = priv->sgmii_port34_regs; - else - sgmii_port_regs = priv->sgmii_port_regs; - - netcp_sgmii_rtreset(sgmii_port_regs, slave->slave_num, set); + netcp_sgmii_rtreset(SGMII_BASE(priv, slave->slave_num), + slave->slave_num, set); } static void gbe_slave_stop(struct gbe_intf *intf) @@ -2136,17 +2126,12 @@ static void gbe_slave_stop(struct gbe_intf *intf) static void gbe_sgmii_config(struct gbe_priv *priv, struct gbe_slave *slave) { - void __iomem *sgmii_port_regs; - - sgmii_port_regs = priv->sgmii_port_regs; - if ((priv->ss_version == GBE_SS_VERSION_14) && (slave->slave_num >= 2)) - sgmii_port_regs = priv->sgmii_port34_regs; + if (SLAVE_LINK_IS_XGMII(slave)) + return; - if (!SLAVE_LINK_IS_XGMII(slave)) { - netcp_sgmii_reset(sgmii_port_regs, slave->slave_num); - netcp_sgmii_config(sgmii_port_regs, slave->slave_num, - slave->link_interface); - } + netcp_sgmii_reset(SGMII_BASE(priv, slave->slave_num), slave->slave_num); + netcp_sgmii_config(SGMII_BASE(priv, slave->slave_num), slave->slave_num, + slave->link_interface); } static int gbe_slave_open(struct gbe_intf *gbe_intf) @@ -2997,6 +2982,14 @@ static int set_gbenu_ethss_priv(struct gbe_priv *gbe_dev, gbe_dev->switch_regs = regs; gbe_dev->sgmii_port_regs = gbe_dev->ss_regs + GBENU_SGMII_MODULE_OFFSET; + + /* Although sgmii modules are mem mapped to one contiguous + * region on GBENU devices, setting sgmii_port34_regs allows + * consistent code when accessing sgmii api + */ + gbe_dev->sgmii_port34_regs = gbe_dev->sgmii_port_regs + + (2 * GBENU_SGMII_MODULE_SIZE); + gbe_dev->host_port_regs = gbe_dev->switch_regs + GBENU_HOST_PORT_OFFSET; for (i = 0; i < (gbe_dev->max_num_ports); i++) diff --git a/drivers/net/ethernet/via/Kconfig b/drivers/net/ethernet/via/Kconfig index 2f1264b882b9..d3d094742a7e 100644 --- a/drivers/net/ethernet/via/Kconfig +++ b/drivers/net/ethernet/via/Kconfig @@ -17,7 +17,7 @@ if NET_VENDOR_VIA config VIA_RHINE tristate "VIA Rhine support" - depends on (PCI || OF_IRQ) + depends on PCI || (OF_IRQ && GENERIC_PCI_IOMAP) depends on HAS_DMA select CRC32 select MII diff --git a/drivers/net/ethernet/xilinx/ll_temac_mdio.c b/drivers/net/ethernet/xilinx/ll_temac_mdio.c index 8cf9d4f56bb2..415de1eaf641 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_mdio.c +++ b/drivers/net/ethernet/xilinx/ll_temac_mdio.c @@ -59,16 +59,15 @@ static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) int temac_mdio_setup(struct temac_local *lp, struct device_node *np) { struct mii_bus *bus; - const u32 *bus_hz; + u32 bus_hz; int clk_div; - int rc, size; + int rc; struct resource res; /* Calculate a reasonable divisor for the clock rate */ clk_div = 0x3f; /* worst-case default setting */ - bus_hz = of_get_property(np, "clock-frequency", &size); - if (bus_hz && size >= sizeof(*bus_hz)) { - clk_div = (*bus_hz) / (2500 * 1000 * 2) - 1; + if (of_property_read_u32(np, "clock-frequency", &bus_hz) == 0) { + clk_div = bus_hz / (2500 * 1000 * 2) - 1; if (clk_div < 1) clk_div = 1; if (clk_div > 0x3f) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index 2a5a16834c01..507bbb0355c2 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -129,7 +129,6 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np) { int ret; u32 clk_div, host_clock; - u32 *property_p; struct mii_bus *bus; struct resource res; struct device_node *np1; @@ -168,8 +167,7 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np) clk_div = DEFAULT_CLOCK_DIVISOR; goto issue; } - property_p = (u32 *) of_get_property(np1, "clock-frequency", NULL); - if (!property_p) { + if (of_property_read_u32(np1, "clock-frequency", &host_clock)) { netdev_warn(lp->ndev, "clock-frequency property not found.\n"); netdev_warn(lp->ndev, "Setting MDIO clock divisor to default %d\n", @@ -179,7 +177,6 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np) goto issue; } - host_clock = be32_to_cpup(property_p); clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1; /* If there is any remainder from the division of * fHOST / (MAX_MDIO_FREQ * 2), then we need to add diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 6008eee01a33..cf468c87ce57 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -828,6 +828,8 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) if (!phydev) dev_info(dev, "MDIO of the phy is not registered yet\n"); + else + put_device(&phydev->dev); return 0; } diff --git a/drivers/net/fjes/fjes_hw.c b/drivers/net/fjes/fjes_hw.c index b5f4a78da828..2d3848c9dc35 100644 --- a/drivers/net/fjes/fjes_hw.c +++ b/drivers/net/fjes/fjes_hw.c @@ -1011,11 +1011,11 @@ static void fjes_hw_update_zone_task(struct work_struct *work) set_bit(epidx, &irq_bit); break; } - } - - hw->ep_shm_info[epidx].es_status = info[epidx].es_status; - hw->ep_shm_info[epidx].zone = info[epidx].zone; + hw->ep_shm_info[epidx].es_status = + info[epidx].es_status; + hw->ep_shm_info[epidx].zone = info[epidx].zone; + } break; } diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index da3259ce7c8d..8f5c02eed47d 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -126,6 +126,8 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb) __be32 addr; int err; + iph = ip_hdr(skb); /* outer IP header... */ + if (gs->collect_md) { static u8 zero_vni[3]; @@ -133,7 +135,6 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb) addr = 0; } else { vni = gnvh->vni; - iph = ip_hdr(skb); /* Still outer IP header... */ addr = iph->saddr; } @@ -178,7 +179,6 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb) skb_reset_network_header(skb); - iph = ip_hdr(skb); /* Now inner IP header... */ err = IP_ECN_decapsulate(iph, skb); if (unlikely(err)) { @@ -626,6 +626,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev) struct geneve_sock *gs = geneve->sock; struct ip_tunnel_info *info = NULL; struct rtable *rt = NULL; + const struct iphdr *iip; /* interior IP header */ struct flowi4 fl4; __u8 tos, ttl; __be16 sport; @@ -653,6 +654,8 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev) sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); skb_reset_mac_header(skb); + iip = ip_hdr(skb); + if (info) { const struct ip_tunnel_key *key = &info->key; u8 *opts = NULL; @@ -668,19 +671,16 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(err)) goto err; - tos = key->tos; + tos = ip_tunnel_ecn_encap(key->tos, iip, skb); ttl = key->ttl; df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; } else { - const struct iphdr *iip; /* interior IP header */ - udp_csum = false; err = geneve_build_skb(rt, skb, 0, geneve->vni, 0, NULL, udp_csum); if (unlikely(err)) goto err; - iip = ip_hdr(skb); tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, iip, skb); ttl = geneve->ttl; if (!ttl && IN_MULTICAST(ntohl(fl4.daddr))) @@ -748,12 +748,8 @@ static void geneve_setup(struct net_device *dev) dev->features |= NETIF_F_RXCSUM; dev->features |= NETIF_F_GSO_SOFTWARE; - dev->vlan_features = dev->features; - dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; - dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_GSO_SOFTWARE; - dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; netif_keep_dst(dev); dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; @@ -819,7 +815,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn, static int geneve_configure(struct net *net, struct net_device *dev, __be32 rem_addr, __u32 vni, __u8 ttl, __u8 tos, - __u16 dst_port, bool metadata) + __be16 dst_port, bool metadata) { struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_dev *t, *geneve = netdev_priv(dev); @@ -844,10 +840,10 @@ static int geneve_configure(struct net *net, struct net_device *dev, geneve->ttl = ttl; geneve->tos = tos; - geneve->dst_port = htons(dst_port); + geneve->dst_port = dst_port; geneve->collect_md = metadata; - t = geneve_find_dev(gn, htons(dst_port), rem_addr, geneve->vni, + t = geneve_find_dev(gn, dst_port, rem_addr, geneve->vni, &tun_on_same_port, &tun_collect_md); if (t) return -EBUSY; @@ -871,7 +867,7 @@ static int geneve_configure(struct net *net, struct net_device *dev, static int geneve_newlink(struct net *net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { - __u16 dst_port = GENEVE_UDP_PORT; + __be16 dst_port = htons(GENEVE_UDP_PORT); __u8 ttl = 0, tos = 0; bool metadata = false; __be32 rem_addr; @@ -890,7 +886,7 @@ static int geneve_newlink(struct net *net, struct net_device *dev, tos = nla_get_u8(data[IFLA_GENEVE_TOS]); if (data[IFLA_GENEVE_PORT]) - dst_port = nla_get_u16(data[IFLA_GENEVE_PORT]); + dst_port = nla_get_be16(data[IFLA_GENEVE_PORT]); if (data[IFLA_GENEVE_COLLECT_METADATA]) metadata = true; @@ -913,7 +909,7 @@ static size_t geneve_get_size(const struct net_device *dev) nla_total_size(sizeof(struct in_addr)) + /* IFLA_GENEVE_REMOTE */ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TTL */ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TOS */ - nla_total_size(sizeof(__u16)) + /* IFLA_GENEVE_PORT */ + nla_total_size(sizeof(__be16)) + /* IFLA_GENEVE_PORT */ nla_total_size(0) + /* IFLA_GENEVE_COLLECT_METADATA */ 0; } @@ -935,7 +931,7 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos)) goto nla_put_failure; - if (nla_put_u16(skb, IFLA_GENEVE_PORT, ntohs(geneve->dst_port))) + if (nla_put_be16(skb, IFLA_GENEVE_PORT, geneve->dst_port)) goto nla_put_failure; if (geneve->collect_md) { @@ -975,7 +971,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name, if (IS_ERR(dev)) return dev; - err = geneve_configure(net, dev, 0, 0, 0, 0, dst_port, true); + err = geneve_configure(net, dev, 0, 0, 0, 0, htons(dst_port), true); if (err) { free_netdev(dev); return ERR_PTR(err); diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig index 1dd5ab8e5054..ce5f1a21e6d7 100644 --- a/drivers/net/ieee802154/Kconfig +++ b/drivers/net/ieee802154/Kconfig @@ -32,10 +32,18 @@ config IEEE802154_AT86RF230 This driver can also be built as a module. To do so, say M here. the module will be called 'at86rf230'. +config IEEE802154_AT86RF230_DEBUGFS + depends on IEEE802154_AT86RF230 + bool "AT86RF230 debugfs interface" + depends on DEBUG_FS + ---help--- + This option compiles debugfs code for the at86rf230 driver. + config IEEE802154_MRF24J40 tristate "Microchip MRF24J40 transceiver driver" depends on IEEE802154_DRIVERS && MAC802154 depends on SPI + select REGMAP_SPI ---help--- Say Y here to enable the MRF24J20 SPI 802.15.4 wireless controller. diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 6422caac8d40..de6e4fa2d6aa 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -31,6 +31,7 @@ #include <linux/skbuff.h> #include <linux/of_gpio.h> #include <linux/ieee802154.h> +#include <linux/debugfs.h> #include <net/mac802154.h> #include <net/cfg802154.h> @@ -80,7 +81,16 @@ struct at86rf230_state_change { u8 from_state; u8 to_state; - bool irq_enable; + bool free; +}; + +struct at86rf230_trac { + u64 success; + u64 success_data_pending; + u64 success_wait_for_ack; + u64 channel_access_failure; + u64 no_ack; + u64 invalid; }; struct at86rf230_local { @@ -95,14 +105,14 @@ struct at86rf230_local { struct completion state_complete; struct at86rf230_state_change state; - struct at86rf230_state_change irq; - unsigned long cal_timeout; bool is_tx; bool is_tx_from_off; u8 tx_retry; struct sk_buff *tx_skb; struct at86rf230_state_change tx; + + struct at86rf230_trac trac; }; #define AT86RF2XX_NUMREGS 0x3F @@ -110,8 +120,7 @@ struct at86rf230_local { static void at86rf230_async_state_change(struct at86rf230_local *lp, struct at86rf230_state_change *ctx, - const u8 state, void (*complete)(void *context), - const bool irq_enable); + const u8 state, void (*complete)(void *context)); static inline void at86rf230_sleep(struct at86rf230_local *lp) @@ -340,8 +349,10 @@ at86rf230_async_error_recover(void *context) struct at86rf230_local *lp = ctx->lp; lp->is_tx = 0; - at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL, false); + at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL); ieee802154_wake_queue(lp->hw); + if (ctx->free) + kfree(ctx); } static inline void @@ -351,15 +362,14 @@ at86rf230_async_error(struct at86rf230_local *lp, dev_err(&lp->spi->dev, "spi_async error %d\n", rc); at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF, - at86rf230_async_error_recover, false); + at86rf230_async_error_recover); } /* Generic function to get some register value in async mode */ static void -at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg, +at86rf230_async_read_reg(struct at86rf230_local *lp, u8 reg, struct at86rf230_state_change *ctx, - void (*complete)(void *context), - const bool irq_enable) + void (*complete)(void *context)) { int rc; @@ -367,22 +377,24 @@ at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg, tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG; ctx->msg.complete = complete; - ctx->irq_enable = irq_enable; rc = spi_async(lp->spi, &ctx->msg); - if (rc) { - if (irq_enable) - enable_irq(ctx->irq); - + if (rc) at86rf230_async_error(lp, ctx, rc); - } } -static inline u8 at86rf230_state_to_force(u8 state) +static void +at86rf230_async_write_reg(struct at86rf230_local *lp, u8 reg, u8 val, + struct at86rf230_state_change *ctx, + void (*complete)(void *context)) { - if (state == STATE_TX_ON) - return STATE_FORCE_TX_ON; - else - return STATE_FORCE_TRX_OFF; + int rc; + + ctx->buf[0] = (reg & CMD_REG_MASK) | CMD_REG | CMD_WRITE; + ctx->buf[1] = val; + ctx->msg.complete = complete; + rc = spi_async(lp->spi, &ctx->msg); + if (rc) + at86rf230_async_error(lp, ctx, rc); } static void @@ -426,12 +438,11 @@ at86rf230_async_state_assert(void *context) u8 state = ctx->to_state; if (lp->tx_retry >= AT86RF2XX_MAX_TX_RETRIES) - state = at86rf230_state_to_force(state); + state = STATE_FORCE_TRX_OFF; lp->tx_retry++; at86rf230_async_state_change(lp, ctx, state, - ctx->complete, - ctx->irq_enable); + ctx->complete); return; } } @@ -452,8 +463,7 @@ static enum hrtimer_restart at86rf230_async_state_timer(struct hrtimer *timer) struct at86rf230_local *lp = ctx->lp; at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx, - at86rf230_async_state_assert, - ctx->irq_enable); + at86rf230_async_state_assert); return HRTIMER_NORESTART; } @@ -558,14 +568,12 @@ at86rf230_async_state_change_start(void *context) struct at86rf230_local *lp = ctx->lp; u8 *buf = ctx->buf; const u8 trx_state = buf[1] & TRX_STATE_MASK; - int rc; /* Check for "possible" STATE_TRANSITION_IN_PROGRESS */ if (trx_state == STATE_TRANSITION_IN_PROGRESS) { udelay(1); at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx, - at86rf230_async_state_change_start, - ctx->irq_enable); + at86rf230_async_state_change_start); return; } @@ -582,31 +590,20 @@ at86rf230_async_state_change_start(void *context) /* Going into the next step for a state change which do a timing * relevant delay. */ - buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE; - buf[1] = ctx->to_state; - ctx->msg.complete = at86rf230_async_state_delay; - rc = spi_async(lp->spi, &ctx->msg); - if (rc) { - if (ctx->irq_enable) - enable_irq(ctx->irq); - - at86rf230_async_error(lp, ctx, rc); - } + at86rf230_async_write_reg(lp, RG_TRX_STATE, ctx->to_state, ctx, + at86rf230_async_state_delay); } static void at86rf230_async_state_change(struct at86rf230_local *lp, struct at86rf230_state_change *ctx, - const u8 state, void (*complete)(void *context), - const bool irq_enable) + const u8 state, void (*complete)(void *context)) { /* Initialization for the state change context */ ctx->to_state = state; ctx->complete = complete; - ctx->irq_enable = irq_enable; at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx, - at86rf230_async_state_change_start, - irq_enable); + at86rf230_async_state_change_start); } static void @@ -628,8 +625,7 @@ at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state) unsigned long rc; at86rf230_async_state_change(lp, &lp->state, state, - at86rf230_sync_state_change_complete, - false); + at86rf230_sync_state_change_complete); rc = wait_for_completion_timeout(&lp->state_complete, msecs_to_jiffies(100)); @@ -647,9 +643,8 @@ at86rf230_tx_complete(void *context) struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; - enable_irq(ctx->irq); - ieee802154_xmit_complete(lp->hw, lp->tx_skb, false); + kfree(ctx); } static void @@ -659,7 +654,7 @@ at86rf230_tx_on(void *context) struct at86rf230_local *lp = ctx->lp; at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, - at86rf230_tx_complete, true); + at86rf230_tx_complete); } static void @@ -667,28 +662,33 @@ at86rf230_tx_trac_check(void *context) { struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; - const u8 *buf = ctx->buf; - const u8 trac = (buf[1] & 0xe0) >> 5; - /* If trac status is different than zero we need to do a state change - * to STATE_FORCE_TRX_OFF then STATE_RX_AACK_ON to recover the - * transceiver. - */ - if (trac) - at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF, - at86rf230_tx_on, true); - else - at86rf230_tx_on(context); -} + if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) { + u8 trac = TRAC_MASK(ctx->buf[1]); -static void -at86rf230_tx_trac_status(void *context) -{ - struct at86rf230_state_change *ctx = context; - struct at86rf230_local *lp = ctx->lp; + switch (trac) { + case TRAC_SUCCESS: + lp->trac.success++; + break; + case TRAC_SUCCESS_DATA_PENDING: + lp->trac.success_data_pending++; + break; + case TRAC_CHANNEL_ACCESS_FAILURE: + lp->trac.channel_access_failure++; + break; + case TRAC_NO_ACK: + lp->trac.no_ack++; + break; + case TRAC_INVALID: + lp->trac.invalid++; + break; + default: + WARN_ONCE(1, "received tx trac status %d\n", trac); + break; + } + } - at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx, - at86rf230_tx_trac_check, true); + at86rf230_async_state_change(lp, ctx, STATE_TX_ON, at86rf230_tx_on); } static void @@ -696,7 +696,6 @@ at86rf230_rx_read_frame_complete(void *context) { struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; - u8 rx_local_buf[AT86RF2XX_MAX_BUF]; const u8 *buf = ctx->buf; struct sk_buff *skb; u8 len, lqi; @@ -708,63 +707,68 @@ at86rf230_rx_read_frame_complete(void *context) } lqi = buf[2 + len]; - memcpy(rx_local_buf, buf + 2, len); - ctx->trx.len = 2; - enable_irq(ctx->irq); - skb = dev_alloc_skb(IEEE802154_MTU); if (!skb) { dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n"); + kfree(ctx); return; } - memcpy(skb_put(skb, len), rx_local_buf, len); + memcpy(skb_put(skb, len), buf + 2, len); ieee802154_rx_irqsafe(lp->hw, skb, lqi); + kfree(ctx); } static void -at86rf230_rx_read_frame(void *context) +at86rf230_rx_trac_check(void *context) { struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; u8 *buf = ctx->buf; int rc; + if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) { + u8 trac = TRAC_MASK(buf[1]); + + switch (trac) { + case TRAC_SUCCESS: + lp->trac.success++; + break; + case TRAC_SUCCESS_WAIT_FOR_ACK: + lp->trac.success_wait_for_ack++; + break; + case TRAC_INVALID: + lp->trac.invalid++; + break; + default: + WARN_ONCE(1, "received rx trac status %d\n", trac); + break; + } + } + buf[0] = CMD_FB; ctx->trx.len = AT86RF2XX_MAX_BUF; ctx->msg.complete = at86rf230_rx_read_frame_complete; rc = spi_async(lp->spi, &ctx->msg); if (rc) { ctx->trx.len = 2; - enable_irq(ctx->irq); at86rf230_async_error(lp, ctx, rc); } } static void -at86rf230_rx_trac_check(void *context) +at86rf230_irq_trx_end(void *context) { - /* Possible check on trac status here. This could be useful to make - * some stats why receive is failed. Not used at the moment, but it's - * maybe timing relevant. Datasheet doesn't say anything about this. - * The programming guide say do it so. - */ - - at86rf230_rx_read_frame(context); -} + struct at86rf230_state_change *ctx = context; + struct at86rf230_local *lp = ctx->lp; -static void -at86rf230_irq_trx_end(struct at86rf230_local *lp) -{ if (lp->is_tx) { lp->is_tx = 0; - at86rf230_async_state_change(lp, &lp->irq, - STATE_FORCE_TX_ON, - at86rf230_tx_trac_status, - true); + at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx, + at86rf230_tx_trac_check); } else { - at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq, - at86rf230_rx_trac_check, true); + at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx, + at86rf230_rx_trac_check); } } @@ -774,32 +778,59 @@ at86rf230_irq_status(void *context) struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; const u8 *buf = ctx->buf; - const u8 irq = buf[1]; + u8 irq = buf[1]; + + enable_irq(lp->spi->irq); if (irq & IRQ_TRX_END) { - at86rf230_irq_trx_end(lp); + at86rf230_irq_trx_end(ctx); } else { - enable_irq(ctx->irq); dev_err(&lp->spi->dev, "not supported irq %02x received\n", irq); + kfree(ctx); } } +static void +at86rf230_setup_spi_messages(struct at86rf230_local *lp, + struct at86rf230_state_change *state) +{ + state->lp = lp; + state->irq = lp->spi->irq; + spi_message_init(&state->msg); + state->msg.context = state; + state->trx.len = 2; + state->trx.tx_buf = state->buf; + state->trx.rx_buf = state->buf; + spi_message_add_tail(&state->trx, &state->msg); + hrtimer_init(&state->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + state->timer.function = at86rf230_async_state_timer; +} + static irqreturn_t at86rf230_isr(int irq, void *data) { struct at86rf230_local *lp = data; - struct at86rf230_state_change *ctx = &lp->irq; - u8 *buf = ctx->buf; + struct at86rf230_state_change *ctx; int rc; disable_irq_nosync(irq); - buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG; + ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC); + if (!ctx) { + enable_irq(irq); + return IRQ_NONE; + } + + at86rf230_setup_spi_messages(lp, ctx); + /* tell on error handling to free ctx */ + ctx->free = true; + + ctx->buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG; ctx->msg.complete = at86rf230_irq_status; rc = spi_async(lp->spi, &ctx->msg); if (rc) { - enable_irq(irq); at86rf230_async_error(lp, ctx, rc); + enable_irq(irq); return IRQ_NONE; } @@ -811,21 +842,14 @@ at86rf230_write_frame_complete(void *context) { struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; - u8 *buf = ctx->buf; - int rc; ctx->trx.len = 2; - if (gpio_is_valid(lp->slp_tr)) { + if (gpio_is_valid(lp->slp_tr)) at86rf230_slp_tr_rising_edge(lp); - } else { - buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE; - buf[1] = STATE_BUSY_TX; - ctx->msg.complete = NULL; - rc = spi_async(lp->spi, &ctx->msg); - if (rc) - at86rf230_async_error(lp, ctx, rc); - } + else + at86rf230_async_write_reg(lp, RG_TRX_STATE, STATE_BUSY_TX, ctx, + NULL); } static void @@ -858,7 +882,7 @@ at86rf230_xmit_tx_on(void *context) struct at86rf230_local *lp = ctx->lp; at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON, - at86rf230_write_frame, false); + at86rf230_write_frame); } static void @@ -871,12 +895,10 @@ at86rf230_xmit_start(void *context) if (lp->is_tx_from_off) { lp->is_tx_from_off = false; at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON, - at86rf230_write_frame, - false); + at86rf230_write_frame); } else { at86rf230_async_state_change(lp, ctx, STATE_TX_ON, - at86rf230_xmit_tx_on, - false); + at86rf230_xmit_tx_on); } } @@ -899,7 +921,7 @@ at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) if (time_is_before_jiffies(lp->cal_timeout)) { lp->is_tx_from_off = true; at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF, - at86rf230_xmit_start, false); + at86rf230_xmit_start); } else { at86rf230_xmit_start(ctx); } @@ -920,6 +942,10 @@ at86rf230_start(struct ieee802154_hw *hw) { struct at86rf230_local *lp = hw->priv; + /* reset trac stats on start */ + if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) + memset(&lp->trac, 0, sizeof(struct at86rf230_trac)); + at86rf230_awake(lp); enable_irq(lp->spi->irq); @@ -1354,10 +1380,6 @@ static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim) return rc; irq_type = irq_get_trigger_type(lp->spi->irq); - if (irq_type == IRQ_TYPE_EDGE_RISING || - irq_type == IRQ_TYPE_EDGE_FALLING) - dev_warn(&lp->spi->dev, - "Using edge triggered irq's are not recommended!\n"); if (irq_type == IRQ_TYPE_EDGE_FALLING || irq_type == IRQ_TYPE_LEVEL_LOW) irq_pol = IRQ_ACTIVE_LOW; @@ -1583,42 +1605,65 @@ not_supp: return rc; } -static void -at86rf230_setup_spi_messages(struct at86rf230_local *lp) +#ifdef CONFIG_IEEE802154_AT86RF230_DEBUGFS +static struct dentry *at86rf230_debugfs_root; + +static int at86rf230_stats_show(struct seq_file *file, void *offset) +{ + struct at86rf230_local *lp = file->private; + + seq_printf(file, "SUCCESS:\t\t%8llu\n", lp->trac.success); + seq_printf(file, "SUCCESS_DATA_PENDING:\t%8llu\n", + lp->trac.success_data_pending); + seq_printf(file, "SUCCESS_WAIT_FOR_ACK:\t%8llu\n", + lp->trac.success_wait_for_ack); + seq_printf(file, "CHANNEL_ACCESS_FAILURE:\t%8llu\n", + lp->trac.channel_access_failure); + seq_printf(file, "NO_ACK:\t\t\t%8llu\n", lp->trac.no_ack); + seq_printf(file, "INVALID:\t\t%8llu\n", lp->trac.invalid); + return 0; +} + +static int at86rf230_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, at86rf230_stats_show, inode->i_private); +} + +static const struct file_operations at86rf230_stats_fops = { + .open = at86rf230_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int at86rf230_debugfs_init(struct at86rf230_local *lp) +{ + char debugfs_dir_name[DNAME_INLINE_LEN + 1] = "at86rf230-"; + struct dentry *stats; + + strncat(debugfs_dir_name, dev_name(&lp->spi->dev), DNAME_INLINE_LEN); + + at86rf230_debugfs_root = debugfs_create_dir(debugfs_dir_name, NULL); + if (!at86rf230_debugfs_root) + return -ENOMEM; + + stats = debugfs_create_file("trac_stats", S_IRUGO, + at86rf230_debugfs_root, lp, + &at86rf230_stats_fops); + if (!stats) + return -ENOMEM; + + return 0; +} + +static void at86rf230_debugfs_remove(void) { - lp->state.lp = lp; - lp->state.irq = lp->spi->irq; - spi_message_init(&lp->state.msg); - lp->state.msg.context = &lp->state; - lp->state.trx.len = 2; - lp->state.trx.tx_buf = lp->state.buf; - lp->state.trx.rx_buf = lp->state.buf; - spi_message_add_tail(&lp->state.trx, &lp->state.msg); - hrtimer_init(&lp->state.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - lp->state.timer.function = at86rf230_async_state_timer; - - lp->irq.lp = lp; - lp->irq.irq = lp->spi->irq; - spi_message_init(&lp->irq.msg); - lp->irq.msg.context = &lp->irq; - lp->irq.trx.len = 2; - lp->irq.trx.tx_buf = lp->irq.buf; - lp->irq.trx.rx_buf = lp->irq.buf; - spi_message_add_tail(&lp->irq.trx, &lp->irq.msg); - hrtimer_init(&lp->irq.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - lp->irq.timer.function = at86rf230_async_state_timer; - - lp->tx.lp = lp; - lp->tx.irq = lp->spi->irq; - spi_message_init(&lp->tx.msg); - lp->tx.msg.context = &lp->tx; - lp->tx.trx.len = 2; - lp->tx.trx.tx_buf = lp->tx.buf; - lp->tx.trx.rx_buf = lp->tx.buf; - spi_message_add_tail(&lp->tx.trx, &lp->tx.msg); - hrtimer_init(&lp->tx.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - lp->tx.timer.function = at86rf230_async_state_timer; + debugfs_remove_recursive(at86rf230_debugfs_root); } +#else +static int at86rf230_debugfs_init(struct at86rf230_local *lp) { return 0; } +static void at86rf230_debugfs_remove(void) { } +#endif static int at86rf230_probe(struct spi_device *spi) { @@ -1681,7 +1726,8 @@ static int at86rf230_probe(struct spi_device *spi) goto free_dev; } - at86rf230_setup_spi_messages(lp); + at86rf230_setup_spi_messages(lp, &lp->state); + at86rf230_setup_spi_messages(lp, &lp->tx); rc = at86rf230_detect_device(lp); if (rc < 0) @@ -1715,12 +1761,18 @@ static int at86rf230_probe(struct spi_device *spi) /* going into sleep by default */ at86rf230_sleep(lp); - rc = ieee802154_register_hw(lp->hw); + rc = at86rf230_debugfs_init(lp); if (rc) goto free_dev; + rc = ieee802154_register_hw(lp->hw); + if (rc) + goto free_debugfs; + return rc; +free_debugfs: + at86rf230_debugfs_remove(); free_dev: ieee802154_free_hw(lp->hw); @@ -1735,6 +1787,7 @@ static int at86rf230_remove(struct spi_device *spi) at86rf230_write_subreg(lp, SR_IRQ_MASK, 0); ieee802154_unregister_hw(lp->hw); ieee802154_free_hw(lp->hw); + at86rf230_debugfs_remove(); dev_dbg(&spi->dev, "unregistered at86rf230\n"); return 0; diff --git a/drivers/net/ieee802154/at86rf230.h b/drivers/net/ieee802154/at86rf230.h index 1e6d1cc677f6..fd9c1f467f63 100644 --- a/drivers/net/ieee802154/at86rf230.h +++ b/drivers/net/ieee802154/at86rf230.h @@ -216,5 +216,13 @@ #define STATE_TRANSITION_IN_PROGRESS 0x1F #define TRX_STATE_MASK (0x1F) +#define TRAC_MASK(x) ((x & 0xe0) >> 5) + +#define TRAC_SUCCESS 0 +#define TRAC_SUCCESS_DATA_PENDING 1 +#define TRAC_SUCCESS_WAIT_FOR_ACK 2 +#define TRAC_CHANNEL_ACCESS_FAILURE 3 +#define TRAC_NO_ACK 5 +#define TRAC_INVALID 7 #endif /* !_AT86RF230_H */ diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index 80dfc725b8dc..199a94a9c8bc 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -559,6 +559,7 @@ static int atusb_get_and_show_chip(struct atusb *atusb) { struct usb_device *usb_dev = atusb->usb_dev; uint8_t man_id_0, man_id_1, part_num, version_num; + const char *chip; man_id_0 = atusb_read_reg(atusb, RG_MAN_ID_0); man_id_1 = atusb_read_reg(atusb, RG_MAN_ID_1); @@ -574,14 +575,22 @@ static int atusb_get_and_show_chip(struct atusb *atusb) man_id_1, man_id_0); goto fail; } - if (part_num != 3 && part_num != 2) { + + switch (part_num) { + case 2: + chip = "AT86RF230"; + break; + case 3: + chip = "AT86RF231"; + break; + default: dev_err(&usb_dev->dev, "unexpected transceiver, part 0x%02x version 0x%02x\n", part_num, version_num); goto fail; } - dev_info(&usb_dev->dev, "ATUSB: AT86RF231 version %d\n", version_num); + dev_info(&usb_dev->dev, "ATUSB: %s version %d\n", chip, version_num); return 0; diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 997724b8e434..aca0fb3cccbf 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -18,51 +18,172 @@ #include <linux/spi/spi.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/regmap.h> #include <linux/ieee802154.h> +#include <linux/irq.h> #include <net/cfg802154.h> #include <net/mac802154.h> /* MRF24J40 Short Address Registers */ -#define REG_RXMCR 0x00 /* Receive MAC control */ -#define REG_PANIDL 0x01 /* PAN ID (low) */ -#define REG_PANIDH 0x02 /* PAN ID (high) */ -#define REG_SADRL 0x03 /* Short address (low) */ -#define REG_SADRH 0x04 /* Short address (high) */ -#define REG_EADR0 0x05 /* Long address (low) (high is EADR7) */ -#define REG_TXMCR 0x11 /* Transmit MAC control */ -#define REG_PACON0 0x16 /* Power Amplifier Control */ -#define REG_PACON1 0x17 /* Power Amplifier Control */ -#define REG_PACON2 0x18 /* Power Amplifier Control */ -#define REG_TXNCON 0x1B /* Transmit Normal FIFO Control */ -#define REG_TXSTAT 0x24 /* TX MAC Status Register */ -#define REG_SOFTRST 0x2A /* Soft Reset */ -#define REG_TXSTBL 0x2E /* TX Stabilization */ -#define REG_INTSTAT 0x31 /* Interrupt Status */ -#define REG_INTCON 0x32 /* Interrupt Control */ -#define REG_GPIO 0x33 /* GPIO */ -#define REG_TRISGPIO 0x34 /* GPIO direction */ -#define REG_RFCTL 0x36 /* RF Control Mode Register */ -#define REG_BBREG1 0x39 /* Baseband Registers */ -#define REG_BBREG2 0x3A /* */ -#define REG_BBREG6 0x3E /* */ -#define REG_CCAEDTH 0x3F /* Energy Detection Threshold */ +#define REG_RXMCR 0x00 /* Receive MAC control */ +#define BIT_PROMI BIT(0) +#define BIT_ERRPKT BIT(1) +#define BIT_NOACKRSP BIT(5) +#define BIT_PANCOORD BIT(3) + +#define REG_PANIDL 0x01 /* PAN ID (low) */ +#define REG_PANIDH 0x02 /* PAN ID (high) */ +#define REG_SADRL 0x03 /* Short address (low) */ +#define REG_SADRH 0x04 /* Short address (high) */ +#define REG_EADR0 0x05 /* Long address (low) (high is EADR7) */ +#define REG_EADR1 0x06 +#define REG_EADR2 0x07 +#define REG_EADR3 0x08 +#define REG_EADR4 0x09 +#define REG_EADR5 0x0A +#define REG_EADR6 0x0B +#define REG_EADR7 0x0C +#define REG_RXFLUSH 0x0D +#define REG_ORDER 0x10 +#define REG_TXMCR 0x11 /* Transmit MAC control */ +#define TXMCR_MIN_BE_SHIFT 3 +#define TXMCR_MIN_BE_MASK 0x18 +#define TXMCR_CSMA_RETRIES_SHIFT 0 +#define TXMCR_CSMA_RETRIES_MASK 0x07 + +#define REG_ACKTMOUT 0x12 +#define REG_ESLOTG1 0x13 +#define REG_SYMTICKL 0x14 +#define REG_SYMTICKH 0x15 +#define REG_PACON0 0x16 /* Power Amplifier Control */ +#define REG_PACON1 0x17 /* Power Amplifier Control */ +#define REG_PACON2 0x18 /* Power Amplifier Control */ +#define REG_TXBCON0 0x1A +#define REG_TXNCON 0x1B /* Transmit Normal FIFO Control */ +#define BIT_TXNTRIG BIT(0) +#define BIT_TXNACKREQ BIT(2) + +#define REG_TXG1CON 0x1C +#define REG_TXG2CON 0x1D +#define REG_ESLOTG23 0x1E +#define REG_ESLOTG45 0x1F +#define REG_ESLOTG67 0x20 +#define REG_TXPEND 0x21 +#define REG_WAKECON 0x22 +#define REG_FROMOFFSET 0x23 +#define REG_TXSTAT 0x24 /* TX MAC Status Register */ +#define REG_TXBCON1 0x25 +#define REG_GATECLK 0x26 +#define REG_TXTIME 0x27 +#define REG_HSYMTMRL 0x28 +#define REG_HSYMTMRH 0x29 +#define REG_SOFTRST 0x2A /* Soft Reset */ +#define REG_SECCON0 0x2C +#define REG_SECCON1 0x2D +#define REG_TXSTBL 0x2E /* TX Stabilization */ +#define REG_RXSR 0x30 +#define REG_INTSTAT 0x31 /* Interrupt Status */ +#define BIT_TXNIF BIT(0) +#define BIT_RXIF BIT(3) + +#define REG_INTCON 0x32 /* Interrupt Control */ +#define BIT_TXNIE BIT(0) +#define BIT_RXIE BIT(3) + +#define REG_GPIO 0x33 /* GPIO */ +#define REG_TRISGPIO 0x34 /* GPIO direction */ +#define REG_SLPACK 0x35 +#define REG_RFCTL 0x36 /* RF Control Mode Register */ +#define BIT_RFRST BIT(2) + +#define REG_SECCR2 0x37 +#define REG_BBREG0 0x38 +#define REG_BBREG1 0x39 /* Baseband Registers */ +#define BIT_RXDECINV BIT(2) + +#define REG_BBREG2 0x3A /* */ +#define BBREG2_CCA_MODE_SHIFT 6 +#define BBREG2_CCA_MODE_MASK 0xc0 + +#define REG_BBREG3 0x3B +#define REG_BBREG4 0x3C +#define REG_BBREG6 0x3E /* */ +#define REG_CCAEDTH 0x3F /* Energy Detection Threshold */ /* MRF24J40 Long Address Registers */ -#define REG_RFCON0 0x200 /* RF Control Registers */ -#define REG_RFCON1 0x201 -#define REG_RFCON2 0x202 -#define REG_RFCON3 0x203 -#define REG_RFCON5 0x205 -#define REG_RFCON6 0x206 -#define REG_RFCON7 0x207 -#define REG_RFCON8 0x208 -#define REG_RSSI 0x210 -#define REG_SLPCON0 0x211 /* Sleep Clock Control Registers */ -#define REG_SLPCON1 0x220 -#define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */ -#define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */ -#define REG_TESTMODE 0x22F /* Test mode */ -#define REG_RX_FIFO 0x300 /* Receive FIFO */ +#define REG_RFCON0 0x200 /* RF Control Registers */ +#define RFCON0_CH_SHIFT 4 +#define RFCON0_CH_MASK 0xf0 +#define RFOPT_RECOMMEND 3 + +#define REG_RFCON1 0x201 +#define REG_RFCON2 0x202 +#define REG_RFCON3 0x203 + +#define TXPWRL_MASK 0xc0 +#define TXPWRL_SHIFT 6 +#define TXPWRL_30 0x3 +#define TXPWRL_20 0x2 +#define TXPWRL_10 0x1 +#define TXPWRL_0 0x0 + +#define TXPWRS_MASK 0x38 +#define TXPWRS_SHIFT 3 +#define TXPWRS_6_3 0x7 +#define TXPWRS_4_9 0x6 +#define TXPWRS_3_7 0x5 +#define TXPWRS_2_8 0x4 +#define TXPWRS_1_9 0x3 +#define TXPWRS_1_2 0x2 +#define TXPWRS_0_5 0x1 +#define TXPWRS_0 0x0 + +#define REG_RFCON5 0x205 +#define REG_RFCON6 0x206 +#define REG_RFCON7 0x207 +#define REG_RFCON8 0x208 +#define REG_SLPCAL0 0x209 +#define REG_SLPCAL1 0x20A +#define REG_SLPCAL2 0x20B +#define REG_RFSTATE 0x20F +#define REG_RSSI 0x210 +#define REG_SLPCON0 0x211 /* Sleep Clock Control Registers */ +#define BIT_INTEDGE BIT(1) + +#define REG_SLPCON1 0x220 +#define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */ +#define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */ +#define REG_REMCNTL 0x224 +#define REG_REMCNTH 0x225 +#define REG_MAINCNT0 0x226 +#define REG_MAINCNT1 0x227 +#define REG_MAINCNT2 0x228 +#define REG_MAINCNT3 0x229 +#define REG_TESTMODE 0x22F /* Test mode */ +#define REG_ASSOEAR0 0x230 +#define REG_ASSOEAR1 0x231 +#define REG_ASSOEAR2 0x232 +#define REG_ASSOEAR3 0x233 +#define REG_ASSOEAR4 0x234 +#define REG_ASSOEAR5 0x235 +#define REG_ASSOEAR6 0x236 +#define REG_ASSOEAR7 0x237 +#define REG_ASSOSAR0 0x238 +#define REG_ASSOSAR1 0x239 +#define REG_UNONCE0 0x240 +#define REG_UNONCE1 0x241 +#define REG_UNONCE2 0x242 +#define REG_UNONCE3 0x243 +#define REG_UNONCE4 0x244 +#define REG_UNONCE5 0x245 +#define REG_UNONCE6 0x246 +#define REG_UNONCE7 0x247 +#define REG_UNONCE8 0x248 +#define REG_UNONCE9 0x249 +#define REG_UNONCE10 0x24A +#define REG_UNONCE11 0x24B +#define REG_UNONCE12 0x24C +#define REG_RX_FIFO 0x300 /* Receive FIFO */ /* Device configuration: Only channels 11-26 on page 0 are supported. */ #define MRF24J40_CHAN_MIN 11 @@ -81,11 +202,52 @@ struct mrf24j40 { struct spi_device *spi; struct ieee802154_hw *hw; - struct mutex buffer_mutex; /* only used to protect buf */ - struct completion tx_complete; - u8 *buf; /* 3 bytes. Used for SPI single-register transfers. */ + struct regmap *regmap_short; + struct regmap *regmap_long; + + /* for writing txfifo */ + struct spi_message tx_msg; + u8 tx_hdr_buf[2]; + struct spi_transfer tx_hdr_trx; + u8 tx_len_buf[2]; + struct spi_transfer tx_len_trx; + struct spi_transfer tx_buf_trx; + struct sk_buff *tx_skb; + + /* post transmit message to send frame out */ + struct spi_message tx_post_msg; + u8 tx_post_buf[2]; + struct spi_transfer tx_post_trx; + + /* for protect/unprotect/read length rxfifo */ + struct spi_message rx_msg; + u8 rx_buf[3]; + struct spi_transfer rx_trx; + + /* receive handling */ + struct spi_message rx_buf_msg; + u8 rx_addr_buf[2]; + struct spi_transfer rx_addr_trx; + u8 rx_lqi_buf[2]; + struct spi_transfer rx_lqi_trx; + u8 rx_fifo_buf[RX_FIFO_SIZE]; + struct spi_transfer rx_fifo_buf_trx; + + /* isr handling for reading intstat */ + struct spi_message irq_msg; + u8 irq_buf[2]; + struct spi_transfer irq_trx; }; +/* regmap information for short address register access */ +#define MRF24J40_SHORT_WRITE 0x01 +#define MRF24J40_SHORT_READ 0x00 +#define MRF24J40_SHORT_NUMREGS 0x3F + +/* regmap information for long address register access */ +#define MRF24J40_LONG_ACCESS 0x80 +#define MRF24J40_LONG_NUMREGS 0x38F + /* Read/Write SPI Commands for Short and Long Address registers. */ #define MRF24J40_READSHORT(reg) ((reg) << 1) #define MRF24J40_WRITESHORT(reg) ((reg) << 1 | 1) @@ -97,118 +259,304 @@ struct mrf24j40 { #define printdev(X) (&X->spi->dev) -static int write_short_reg(struct mrf24j40 *devrec, u8 reg, u8 value) +static bool +mrf24j40_short_reg_writeable(struct device *dev, unsigned int reg) { - int ret; - struct spi_message msg; - struct spi_transfer xfer = { - .len = 2, - .tx_buf = devrec->buf, - .rx_buf = devrec->buf, - }; + switch (reg) { + case REG_RXMCR: + case REG_PANIDL: + case REG_PANIDH: + case REG_SADRL: + case REG_SADRH: + case REG_EADR0: + case REG_EADR1: + case REG_EADR2: + case REG_EADR3: + case REG_EADR4: + case REG_EADR5: + case REG_EADR6: + case REG_EADR7: + case REG_RXFLUSH: + case REG_ORDER: + case REG_TXMCR: + case REG_ACKTMOUT: + case REG_ESLOTG1: + case REG_SYMTICKL: + case REG_SYMTICKH: + case REG_PACON0: + case REG_PACON1: + case REG_PACON2: + case REG_TXBCON0: + case REG_TXNCON: + case REG_TXG1CON: + case REG_TXG2CON: + case REG_ESLOTG23: + case REG_ESLOTG45: + case REG_ESLOTG67: + case REG_TXPEND: + case REG_WAKECON: + case REG_FROMOFFSET: + case REG_TXBCON1: + case REG_GATECLK: + case REG_TXTIME: + case REG_HSYMTMRL: + case REG_HSYMTMRH: + case REG_SOFTRST: + case REG_SECCON0: + case REG_SECCON1: + case REG_TXSTBL: + case REG_RXSR: + case REG_INTCON: + case REG_TRISGPIO: + case REG_GPIO: + case REG_RFCTL: + case REG_SLPACK: + case REG_BBREG0: + case REG_BBREG1: + case REG_BBREG2: + case REG_BBREG3: + case REG_BBREG4: + case REG_BBREG6: + case REG_CCAEDTH: + return true; + default: + return false; + } +} - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); +static bool +mrf24j40_short_reg_readable(struct device *dev, unsigned int reg) +{ + bool rc; + + /* all writeable are also readable */ + rc = mrf24j40_short_reg_writeable(dev, reg); + if (rc) + return rc; + + /* readonly regs */ + switch (reg) { + case REG_TXSTAT: + case REG_INTSTAT: + return true; + default: + return false; + } +} - mutex_lock(&devrec->buffer_mutex); - devrec->buf[0] = MRF24J40_WRITESHORT(reg); - devrec->buf[1] = value; +static bool +mrf24j40_short_reg_volatile(struct device *dev, unsigned int reg) +{ + /* can be changed during runtime */ + switch (reg) { + case REG_TXSTAT: + case REG_INTSTAT: + case REG_RXFLUSH: + case REG_TXNCON: + case REG_SOFTRST: + case REG_RFCTL: + case REG_TXBCON0: + case REG_TXG1CON: + case REG_TXG2CON: + case REG_TXBCON1: + case REG_SECCON0: + case REG_RXSR: + case REG_SLPACK: + case REG_SECCR2: + case REG_BBREG6: + /* use them in spi_async and regmap so it's volatile */ + case REG_BBREG1: + return true; + default: + return false; + } +} - ret = spi_sync(devrec->spi, &msg); - if (ret) - dev_err(printdev(devrec), - "SPI write Failed for short register 0x%hhx\n", reg); +static bool +mrf24j40_short_reg_precious(struct device *dev, unsigned int reg) +{ + /* don't clear irq line on read */ + switch (reg) { + case REG_INTSTAT: + return true; + default: + return false; + } +} - mutex_unlock(&devrec->buffer_mutex); - return ret; +static const struct regmap_config mrf24j40_short_regmap = { + .name = "mrf24j40_short", + .reg_bits = 7, + .val_bits = 8, + .pad_bits = 1, + .write_flag_mask = MRF24J40_SHORT_WRITE, + .read_flag_mask = MRF24J40_SHORT_READ, + .cache_type = REGCACHE_RBTREE, + .max_register = MRF24J40_SHORT_NUMREGS, + .writeable_reg = mrf24j40_short_reg_writeable, + .readable_reg = mrf24j40_short_reg_readable, + .volatile_reg = mrf24j40_short_reg_volatile, + .precious_reg = mrf24j40_short_reg_precious, +}; + +static bool +mrf24j40_long_reg_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_RFCON0: + case REG_RFCON1: + case REG_RFCON2: + case REG_RFCON3: + case REG_RFCON5: + case REG_RFCON6: + case REG_RFCON7: + case REG_RFCON8: + case REG_SLPCAL2: + case REG_SLPCON0: + case REG_SLPCON1: + case REG_WAKETIMEL: + case REG_WAKETIMEH: + case REG_REMCNTL: + case REG_REMCNTH: + case REG_MAINCNT0: + case REG_MAINCNT1: + case REG_MAINCNT2: + case REG_MAINCNT3: + case REG_TESTMODE: + case REG_ASSOEAR0: + case REG_ASSOEAR1: + case REG_ASSOEAR2: + case REG_ASSOEAR3: + case REG_ASSOEAR4: + case REG_ASSOEAR5: + case REG_ASSOEAR6: + case REG_ASSOEAR7: + case REG_ASSOSAR0: + case REG_ASSOSAR1: + case REG_UNONCE0: + case REG_UNONCE1: + case REG_UNONCE2: + case REG_UNONCE3: + case REG_UNONCE4: + case REG_UNONCE5: + case REG_UNONCE6: + case REG_UNONCE7: + case REG_UNONCE8: + case REG_UNONCE9: + case REG_UNONCE10: + case REG_UNONCE11: + case REG_UNONCE12: + return true; + default: + return false; + } } -static int read_short_reg(struct mrf24j40 *devrec, u8 reg, u8 *val) +static bool +mrf24j40_long_reg_readable(struct device *dev, unsigned int reg) { - int ret = -1; - struct spi_message msg; - struct spi_transfer xfer = { - .len = 2, - .tx_buf = devrec->buf, - .rx_buf = devrec->buf, - }; + bool rc; + + /* all writeable are also readable */ + rc = mrf24j40_long_reg_writeable(dev, reg); + if (rc) + return rc; + + /* readonly regs */ + switch (reg) { + case REG_SLPCAL0: + case REG_SLPCAL1: + case REG_RFSTATE: + case REG_RSSI: + return true; + default: + return false; + } +} - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); +static bool +mrf24j40_long_reg_volatile(struct device *dev, unsigned int reg) +{ + /* can be changed during runtime */ + switch (reg) { + case REG_SLPCAL0: + case REG_SLPCAL1: + case REG_SLPCAL2: + case REG_RFSTATE: + case REG_RSSI: + case REG_MAINCNT3: + return true; + default: + return false; + } +} - mutex_lock(&devrec->buffer_mutex); - devrec->buf[0] = MRF24J40_READSHORT(reg); - devrec->buf[1] = 0; +static const struct regmap_config mrf24j40_long_regmap = { + .name = "mrf24j40_long", + .reg_bits = 11, + .val_bits = 8, + .pad_bits = 5, + .write_flag_mask = MRF24J40_LONG_ACCESS, + .read_flag_mask = MRF24J40_LONG_ACCESS, + .cache_type = REGCACHE_RBTREE, + .max_register = MRF24J40_LONG_NUMREGS, + .writeable_reg = mrf24j40_long_reg_writeable, + .readable_reg = mrf24j40_long_reg_readable, + .volatile_reg = mrf24j40_long_reg_volatile, +}; - ret = spi_sync(devrec->spi, &msg); - if (ret) - dev_err(printdev(devrec), - "SPI read Failed for short register 0x%hhx\n", reg); - else - *val = devrec->buf[1]; +static int mrf24j40_long_regmap_write(void *context, const void *data, + size_t count) +{ + struct spi_device *spi = context; + u8 buf[3]; - mutex_unlock(&devrec->buffer_mutex); - return ret; + if (count > 3) + return -EINVAL; + + /* regmap supports read/write mask only in frist byte + * long write access need to set the 12th bit, so we + * make special handling for write. + */ + memcpy(buf, data, count); + buf[1] |= (1 << 4); + + return spi_write(spi, buf, count); } -static int read_long_reg(struct mrf24j40 *devrec, u16 reg, u8 *value) +static int +mrf24j40_long_regmap_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) { - int ret; - u16 cmd; - struct spi_message msg; - struct spi_transfer xfer = { - .len = 3, - .tx_buf = devrec->buf, - .rx_buf = devrec->buf, - }; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - cmd = MRF24J40_READLONG(reg); - mutex_lock(&devrec->buffer_mutex); - devrec->buf[0] = cmd >> 8 & 0xff; - devrec->buf[1] = cmd & 0xff; - devrec->buf[2] = 0; - - ret = spi_sync(devrec->spi, &msg); - if (ret) - dev_err(printdev(devrec), - "SPI read Failed for long register 0x%hx\n", reg); - else - *value = devrec->buf[2]; + struct spi_device *spi = context; - mutex_unlock(&devrec->buffer_mutex); - return ret; + return spi_write_then_read(spi, reg, reg_size, val, val_size); } -static int write_long_reg(struct mrf24j40 *devrec, u16 reg, u8 val) +static const struct regmap_bus mrf24j40_long_regmap_bus = { + .write = mrf24j40_long_regmap_write, + .read = mrf24j40_long_regmap_read, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static void write_tx_buf_complete(void *context) { + struct mrf24j40 *devrec = context; + __le16 fc = ieee802154_get_fc_from_skb(devrec->tx_skb); + u8 val = BIT_TXNTRIG; int ret; - u16 cmd; - struct spi_message msg; - struct spi_transfer xfer = { - .len = 3, - .tx_buf = devrec->buf, - .rx_buf = devrec->buf, - }; - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); + if (ieee802154_is_ackreq(fc)) + val |= BIT_TXNACKREQ; - cmd = MRF24J40_WRITELONG(reg); - mutex_lock(&devrec->buffer_mutex); - devrec->buf[0] = cmd >> 8 & 0xff; - devrec->buf[1] = cmd & 0xff; - devrec->buf[2] = val; + devrec->tx_post_msg.complete = NULL; + devrec->tx_post_buf[0] = MRF24J40_WRITESHORT(REG_TXNCON); + devrec->tx_post_buf[1] = val; - ret = spi_sync(devrec->spi, &msg); + ret = spi_async(devrec->spi, &devrec->tx_post_msg); if (ret) - dev_err(printdev(devrec), - "SPI write Failed for long register 0x%hx\n", reg); - - mutex_unlock(&devrec->buffer_mutex); - return ret; + dev_err(printdev(devrec), "SPI write Failed for transmit buf\n"); } /* This function relies on an undocumented write method. Once a write command @@ -217,22 +565,8 @@ static int write_long_reg(struct mrf24j40 *devrec, u16 reg, u8 val) static int write_tx_buf(struct mrf24j40 *devrec, u16 reg, const u8 *data, size_t length) { - int ret; u16 cmd; - u8 lengths[2]; - struct spi_message msg; - struct spi_transfer addr_xfer = { - .len = 2, - .tx_buf = devrec->buf, - }; - struct spi_transfer lengths_xfer = { - .len = 2, - .tx_buf = &lengths, /* TODO: Is DMA really required for SPI? */ - }; - struct spi_transfer data_xfer = { - .len = length, - .tx_buf = data, - }; + int ret; /* Range check the length. 2 bytes are used for the length fields.*/ if (length > TX_FIFO_SIZE-2) { @@ -240,147 +574,29 @@ static int write_tx_buf(struct mrf24j40 *devrec, u16 reg, length = TX_FIFO_SIZE-2; } - spi_message_init(&msg); - spi_message_add_tail(&addr_xfer, &msg); - spi_message_add_tail(&lengths_xfer, &msg); - spi_message_add_tail(&data_xfer, &msg); - cmd = MRF24J40_WRITELONG(reg); - mutex_lock(&devrec->buffer_mutex); - devrec->buf[0] = cmd >> 8 & 0xff; - devrec->buf[1] = cmd & 0xff; - lengths[0] = 0x0; /* Header Length. Set to 0 for now. TODO */ - lengths[1] = length; /* Total length */ - - ret = spi_sync(devrec->spi, &msg); + devrec->tx_hdr_buf[0] = cmd >> 8 & 0xff; + devrec->tx_hdr_buf[1] = cmd & 0xff; + devrec->tx_len_buf[0] = 0x0; /* Header Length. Set to 0 for now. TODO */ + devrec->tx_len_buf[1] = length; /* Total length */ + devrec->tx_buf_trx.tx_buf = data; + devrec->tx_buf_trx.len = length; + + ret = spi_async(devrec->spi, &devrec->tx_msg); if (ret) dev_err(printdev(devrec), "SPI write Failed for TX buf\n"); - mutex_unlock(&devrec->buffer_mutex); - return ret; -} - -static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec, - u8 *data, u8 *len, u8 *lqi) -{ - u8 rx_len; - u8 addr[2]; - u8 lqi_rssi[2]; - u16 cmd; - int ret; - struct spi_message msg; - struct spi_transfer addr_xfer = { - .len = 2, - .tx_buf = &addr, - }; - struct spi_transfer data_xfer = { - .len = 0x0, /* set below */ - .rx_buf = data, - }; - struct spi_transfer status_xfer = { - .len = 2, - .rx_buf = &lqi_rssi, - }; - - /* Get the length of the data in the RX FIFO. The length in this - * register exclues the 1-byte length field at the beginning. */ - ret = read_long_reg(devrec, REG_RX_FIFO, &rx_len); - if (ret) - goto out; - - /* Range check the RX FIFO length, accounting for the one-byte - * length field at the beginning. */ - if (rx_len > RX_FIFO_SIZE-1) { - dev_err(printdev(devrec), "Invalid length read from device. Performing short read.\n"); - rx_len = RX_FIFO_SIZE-1; - } - - if (rx_len > *len) { - /* Passed in buffer wasn't big enough. Should never happen. */ - dev_err(printdev(devrec), "Buffer not big enough. Performing short read\n"); - rx_len = *len; - } - - /* Set up the commands to read the data. */ - cmd = MRF24J40_READLONG(REG_RX_FIFO+1); - addr[0] = cmd >> 8 & 0xff; - addr[1] = cmd & 0xff; - data_xfer.len = rx_len; - - spi_message_init(&msg); - spi_message_add_tail(&addr_xfer, &msg); - spi_message_add_tail(&data_xfer, &msg); - spi_message_add_tail(&status_xfer, &msg); - - ret = spi_sync(devrec->spi, &msg); - if (ret) { - dev_err(printdev(devrec), "SPI RX Buffer Read Failed.\n"); - goto out; - } - - *lqi = lqi_rssi[0]; - *len = rx_len; - -#ifdef DEBUG - print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ", - DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0); - pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n", - lqi_rssi[0], lqi_rssi[1]); -#endif - -out: return ret; } static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb) { struct mrf24j40 *devrec = hw->priv; - u8 val; - int ret = 0; dev_dbg(printdev(devrec), "tx packet of %d bytes\n", skb->len); + devrec->tx_skb = skb; - ret = write_tx_buf(devrec, 0x000, skb->data, skb->len); - if (ret) - goto err; - - reinit_completion(&devrec->tx_complete); - - /* Set TXNTRIG bit of TXNCON to send packet */ - ret = read_short_reg(devrec, REG_TXNCON, &val); - if (ret) - goto err; - val |= 0x1; - /* Set TXNACKREQ if the ACK bit is set in the packet. */ - if (skb->data[0] & IEEE802154_FC_ACK_REQ) - val |= 0x4; - write_short_reg(devrec, REG_TXNCON, val); - - /* Wait for the device to send the TX complete interrupt. */ - ret = wait_for_completion_interruptible_timeout( - &devrec->tx_complete, - 5 * HZ); - if (ret == -ERESTARTSYS) - goto err; - if (ret == 0) { - dev_warn(printdev(devrec), "Timeout waiting for TX interrupt\n"); - ret = -ETIMEDOUT; - goto err; - } - - /* Check for send error from the device. */ - ret = read_short_reg(devrec, REG_TXSTAT, &val); - if (ret) - goto err; - if (val & 0x1) { - dev_dbg(printdev(devrec), "Error Sending. Retry count exceeded\n"); - ret = -ECOMM; /* TODO: Better error code ? */ - } else - dev_dbg(printdev(devrec), "Packet Sent\n"); - -err: - - return ret; + return write_tx_buf(devrec, 0x000, skb->data, skb->len); } static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level) @@ -394,33 +610,23 @@ static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level) static int mrf24j40_start(struct ieee802154_hw *hw) { struct mrf24j40 *devrec = hw->priv; - u8 val; - int ret; dev_dbg(printdev(devrec), "start\n"); - ret = read_short_reg(devrec, REG_INTCON, &val); - if (ret) - return ret; - val &= ~(0x1|0x8); /* Clear TXNIE and RXIE. Enable interrupts */ - write_short_reg(devrec, REG_INTCON, val); - - return 0; + /* Clear TXNIE and RXIE. Enable interrupts */ + return regmap_update_bits(devrec->regmap_short, REG_INTCON, + BIT_TXNIE | BIT_RXIE, 0); } static void mrf24j40_stop(struct ieee802154_hw *hw) { struct mrf24j40 *devrec = hw->priv; - u8 val; - int ret; dev_dbg(printdev(devrec), "stop\n"); - ret = read_short_reg(devrec, REG_INTCON, &val); - if (ret) - return; - val |= 0x1|0x8; /* Set TXNIE and RXIE. Disable Interrupts */ - write_short_reg(devrec, REG_INTCON, val); + /* Set TXNIE and RXIE. Disable Interrupts */ + regmap_update_bits(devrec->regmap_short, REG_INTCON, + BIT_TXNIE | BIT_TXNIE, BIT_TXNIE | BIT_TXNIE); } static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) @@ -436,21 +642,23 @@ static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) WARN_ON(channel > MRF24J40_CHAN_MAX); /* Set Channel TODO */ - val = (channel-11) << 4 | 0x03; - write_long_reg(devrec, REG_RFCON0, val); + val = (channel - 11) << RFCON0_CH_SHIFT | RFOPT_RECOMMEND; + ret = regmap_update_bits(devrec->regmap_long, REG_RFCON0, + RFCON0_CH_MASK, val); + if (ret) + return ret; /* RF Reset */ - ret = read_short_reg(devrec, REG_RFCTL, &val); + ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST, + BIT_RFRST); if (ret) return ret; - val |= 0x04; - write_short_reg(devrec, REG_RFCTL, val); - val &= ~0x04; - write_short_reg(devrec, REG_RFCTL, val); - udelay(SET_CHANNEL_DELAY_US); /* per datasheet */ + ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST, 0); + if (!ret) + udelay(SET_CHANNEL_DELAY_US); /* per datasheet */ - return 0; + return ret; } static int mrf24j40_filter(struct ieee802154_hw *hw, @@ -468,8 +676,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, addrh = le16_to_cpu(filt->short_addr) >> 8 & 0xff; addrl = le16_to_cpu(filt->short_addr) & 0xff; - write_short_reg(devrec, REG_SADRH, addrh); - write_short_reg(devrec, REG_SADRL, addrl); + regmap_write(devrec->regmap_short, REG_SADRH, addrh); + regmap_write(devrec->regmap_short, REG_SADRL, addrl); dev_dbg(printdev(devrec), "Set short addr to %04hx\n", filt->short_addr); } @@ -480,7 +688,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, memcpy(addr, &filt->ieee_addr, 8); for (i = 0; i < 8; i++) - write_short_reg(devrec, REG_EADR0 + i, addr[i]); + regmap_write(devrec->regmap_short, REG_EADR0 + i, + addr[i]); #ifdef DEBUG pr_debug("Set long addr to: "); @@ -496,8 +705,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, panidh = le16_to_cpu(filt->pan_id) >> 8 & 0xff; panidl = le16_to_cpu(filt->pan_id) & 0xff; - write_short_reg(devrec, REG_PANIDH, panidh); - write_short_reg(devrec, REG_PANIDL, panidl); + regmap_write(devrec->regmap_short, REG_PANIDH, panidh); + regmap_write(devrec->regmap_short, REG_PANIDL, panidl); dev_dbg(printdev(devrec), "Set PANID to %04hx\n", filt->pan_id); } @@ -507,14 +716,14 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, u8 val; int ret; - ret = read_short_reg(devrec, REG_RXMCR, &val); - if (ret) - return ret; if (filt->pan_coord) - val |= 0x8; + val = BIT_PANCOORD; else - val &= ~0x8; - write_short_reg(devrec, REG_RXMCR, val); + val = 0; + ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, + BIT_PANCOORD, val); + if (ret) + return ret; /* REG_SLOTTED is maintained as default (unslotted/CSMA-CA). * REG_ORDER is maintained as default (no beacon/superframe). @@ -527,168 +736,392 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, return 0; } -static int mrf24j40_handle_rx(struct mrf24j40 *devrec) +static void mrf24j40_handle_rx_read_buf_unlock(struct mrf24j40 *devrec) { - u8 len = RX_FIFO_SIZE; - u8 lqi = 0; - u8 val; - int ret = 0; - int ret2; - struct sk_buff *skb; + int ret; - /* Turn off reception of packets off the air. This prevents the - * device from overwriting the buffer while we're reading it. */ - ret = read_short_reg(devrec, REG_BBREG1, &val); + /* Turn back on reception of packets off the air. */ + devrec->rx_msg.complete = NULL; + devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1); + devrec->rx_buf[1] = 0x00; /* CLR RXDECINV */ + ret = spi_async(devrec->spi, &devrec->rx_msg); if (ret) - goto out; - val |= 4; /* SET RXDECINV */ - write_short_reg(devrec, REG_BBREG1, val); + dev_err(printdev(devrec), "failed to unlock rx buffer\n"); +} + +static void mrf24j40_handle_rx_read_buf_complete(void *context) +{ + struct mrf24j40 *devrec = context; + u8 len = devrec->rx_buf[2]; + u8 rx_local_buf[RX_FIFO_SIZE]; + struct sk_buff *skb; + + memcpy(rx_local_buf, devrec->rx_fifo_buf, len); + mrf24j40_handle_rx_read_buf_unlock(devrec); - skb = dev_alloc_skb(len); + skb = dev_alloc_skb(IEEE802154_MTU); if (!skb) { - ret = -ENOMEM; - goto out; + dev_err(printdev(devrec), "failed to allocate skb\n"); + return; } - ret = mrf24j40_read_rx_buf(devrec, skb_put(skb, len), &len, &lqi); - if (ret < 0) { - dev_err(printdev(devrec), "Failure reading RX FIFO\n"); - kfree_skb(skb); - ret = -EINVAL; - goto out; + memcpy(skb_put(skb, len), rx_local_buf, len); + ieee802154_rx_irqsafe(devrec->hw, skb, 0); + +#ifdef DEBUG + print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ", DUMP_PREFIX_OFFSET, 16, 1, + rx_local_buf, len, 0); + pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n", + devrec->rx_lqi_buf[0], devrec->rx_lqi_buf[1]); +#endif +} + +static void mrf24j40_handle_rx_read_buf(void *context) +{ + struct mrf24j40 *devrec = context; + u16 cmd; + int ret; + + /* if length is invalid read the full MTU */ + if (!ieee802154_is_valid_psdu_len(devrec->rx_buf[2])) + devrec->rx_buf[2] = IEEE802154_MTU; + + cmd = MRF24J40_READLONG(REG_RX_FIFO + 1); + devrec->rx_addr_buf[0] = cmd >> 8 & 0xff; + devrec->rx_addr_buf[1] = cmd & 0xff; + devrec->rx_fifo_buf_trx.len = devrec->rx_buf[2]; + ret = spi_async(devrec->spi, &devrec->rx_buf_msg); + if (ret) { + dev_err(printdev(devrec), "failed to read rx buffer\n"); + mrf24j40_handle_rx_read_buf_unlock(devrec); } +} - /* Cut off the checksum */ - skb_trim(skb, len-2); +static void mrf24j40_handle_rx_read_len(void *context) +{ + struct mrf24j40 *devrec = context; + u16 cmd; + int ret; - /* TODO: Other drivers call ieee20154_rx_irqsafe() here (eg: cc2040, - * also from a workqueue). I think irqsafe is not necessary here. - * Can someone confirm? */ - ieee802154_rx_irqsafe(devrec->hw, skb, lqi); + /* read the length of received frame */ + devrec->rx_msg.complete = mrf24j40_handle_rx_read_buf; + devrec->rx_trx.len = 3; + cmd = MRF24J40_READLONG(REG_RX_FIFO); + devrec->rx_buf[0] = cmd >> 8 & 0xff; + devrec->rx_buf[1] = cmd & 0xff; - dev_dbg(printdev(devrec), "RX Handled\n"); + ret = spi_async(devrec->spi, &devrec->rx_msg); + if (ret) { + dev_err(printdev(devrec), "failed to read rx buffer length\n"); + mrf24j40_handle_rx_read_buf_unlock(devrec); + } +} -out: - /* Turn back on reception of packets off the air. */ - ret2 = read_short_reg(devrec, REG_BBREG1, &val); - if (ret2) - return ret2; - val &= ~0x4; /* Clear RXDECINV */ - write_short_reg(devrec, REG_BBREG1, val); +static int mrf24j40_handle_rx(struct mrf24j40 *devrec) +{ + /* Turn off reception of packets off the air. This prevents the + * device from overwriting the buffer while we're reading it. + */ + devrec->rx_msg.complete = mrf24j40_handle_rx_read_len; + devrec->rx_trx.len = 2; + devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1); + devrec->rx_buf[1] = BIT_RXDECINV; /* SET RXDECINV */ + + return spi_async(devrec->spi, &devrec->rx_msg); +} + +static int +mrf24j40_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be, + u8 retries) +{ + struct mrf24j40 *devrec = hw->priv; + u8 val; + + /* min_be */ + val = min_be << TXMCR_MIN_BE_SHIFT; + /* csma backoffs */ + val |= retries << TXMCR_CSMA_RETRIES_SHIFT; + + return regmap_update_bits(devrec->regmap_short, REG_TXMCR, + TXMCR_MIN_BE_MASK | TXMCR_CSMA_RETRIES_MASK, + val); +} + +static int mrf24j40_set_cca_mode(struct ieee802154_hw *hw, + const struct wpan_phy_cca *cca) +{ + struct mrf24j40 *devrec = hw->priv; + u8 val; + + /* mapping 802.15.4 to driver spec */ + switch (cca->mode) { + case NL802154_CCA_ENERGY: + val = 2; + break; + case NL802154_CCA_CARRIER: + val = 1; + break; + case NL802154_CCA_ENERGY_CARRIER: + switch (cca->opt) { + case NL802154_CCA_OPT_ENERGY_CARRIER_AND: + val = 3; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return regmap_update_bits(devrec->regmap_short, REG_BBREG2, + BBREG2_CCA_MODE_MASK, + val << BBREG2_CCA_MODE_SHIFT); +} + +/* array for representing ed levels */ +static const s32 mrf24j40_ed_levels[] = { + -9000, -8900, -8800, -8700, -8600, -8500, -8400, -8300, -8200, -8100, + -8000, -7900, -7800, -7700, -7600, -7500, -7400, -7300, -7200, -7100, + -7000, -6900, -6800, -6700, -6600, -6500, -6400, -6300, -6200, -6100, + -6000, -5900, -5800, -5700, -5600, -5500, -5400, -5300, -5200, -5100, + -5000, -4900, -4800, -4700, -4600, -4500, -4400, -4300, -4200, -4100, + -4000, -3900, -3800, -3700, -3600, -3500 +}; + +/* map ed levels to register value */ +static const s32 mrf24j40_ed_levels_map[][2] = { + { -9000, 0 }, { -8900, 1 }, { -8800, 2 }, { -8700, 5 }, { -8600, 9 }, + { -8500, 13 }, { -8400, 18 }, { -8300, 23 }, { -8200, 27 }, + { -8100, 32 }, { -8000, 37 }, { -7900, 43 }, { -7800, 48 }, + { -7700, 53 }, { -7600, 58 }, { -7500, 63 }, { -7400, 68 }, + { -7300, 73 }, { -7200, 78 }, { -7100, 83 }, { -7000, 89 }, + { -6900, 95 }, { -6800, 100 }, { -6700, 107 }, { -6600, 111 }, + { -6500, 117 }, { -6400, 121 }, { -6300, 125 }, { -6200, 129 }, + { -6100, 133 }, { -6000, 138 }, { -5900, 143 }, { -5800, 148 }, + { -5700, 153 }, { -5600, 159 }, { -5500, 165 }, { -5400, 170 }, + { -5300, 176 }, { -5200, 183 }, { -5100, 188 }, { -5000, 193 }, + { -4900, 198 }, { -4800, 203 }, { -4700, 207 }, { -4600, 212 }, + { -4500, 216 }, { -4400, 221 }, { -4300, 225 }, { -4200, 228 }, + { -4100, 233 }, { -4000, 239 }, { -3900, 245 }, { -3800, 250 }, + { -3700, 253 }, { -3600, 254 }, { -3500, 255 }, +}; + +static int mrf24j40_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm) +{ + struct mrf24j40 *devrec = hw->priv; + int i; + + for (i = 0; i < ARRAY_SIZE(mrf24j40_ed_levels_map); i++) { + if (mrf24j40_ed_levels_map[i][0] == mbm) + return regmap_write(devrec->regmap_short, REG_CCAEDTH, + mrf24j40_ed_levels_map[i][1]); + } + + return -EINVAL; +} + +static const s32 mrf24j40ma_powers[] = { + 0, -50, -120, -190, -280, -370, -490, -630, -1000, -1050, -1120, -1190, + -1280, -1370, -1490, -1630, -2000, -2050, -2120, -2190, -2280, -2370, + -2490, -2630, -3000, -3050, -3120, -3190, -3280, -3370, -3490, -3630, +}; + +static int mrf24j40_set_txpower(struct ieee802154_hw *hw, s32 mbm) +{ + struct mrf24j40 *devrec = hw->priv; + s32 small_scale; + u8 val; + + if (0 >= mbm && mbm > -1000) { + val = TXPWRL_0 << TXPWRL_SHIFT; + small_scale = mbm; + } else if (-1000 >= mbm && mbm > -2000) { + val = TXPWRL_10 << TXPWRL_SHIFT; + small_scale = mbm + 1000; + } else if (-2000 >= mbm && mbm > -3000) { + val = TXPWRL_20 << TXPWRL_SHIFT; + small_scale = mbm + 2000; + } else if (-3000 >= mbm && mbm > -4000) { + val = TXPWRL_30 << TXPWRL_SHIFT; + small_scale = mbm + 3000; + } else { + return -EINVAL; + } + + switch (small_scale) { + case 0: + val |= (TXPWRS_0 << TXPWRS_SHIFT); + break; + case -50: + val |= (TXPWRS_0_5 << TXPWRS_SHIFT); + break; + case -120: + val |= (TXPWRS_1_2 << TXPWRS_SHIFT); + break; + case -190: + val |= (TXPWRS_1_9 << TXPWRS_SHIFT); + break; + case -280: + val |= (TXPWRS_2_8 << TXPWRS_SHIFT); + break; + case -370: + val |= (TXPWRS_3_7 << TXPWRS_SHIFT); + break; + case -490: + val |= (TXPWRS_4_9 << TXPWRS_SHIFT); + break; + case -630: + val |= (TXPWRS_6_3 << TXPWRS_SHIFT); + break; + default: + return -EINVAL; + } + + return regmap_update_bits(devrec->regmap_long, REG_RFCON3, + TXPWRL_MASK | TXPWRS_MASK, val); +} + +static int mrf24j40_set_promiscuous_mode(struct ieee802154_hw *hw, bool on) +{ + struct mrf24j40 *devrec = hw->priv; + int ret; + + if (on) { + /* set PROMI, ERRPKT and NOACKRSP */ + ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, + BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP, + BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP); + } else { + /* clear PROMI, ERRPKT and NOACKRSP */ + ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, + BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP, + 0); + } return ret; } static const struct ieee802154_ops mrf24j40_ops = { .owner = THIS_MODULE, - .xmit_sync = mrf24j40_tx, + .xmit_async = mrf24j40_tx, .ed = mrf24j40_ed, .start = mrf24j40_start, .stop = mrf24j40_stop, .set_channel = mrf24j40_set_channel, .set_hw_addr_filt = mrf24j40_filter, + .set_csma_params = mrf24j40_csma_params, + .set_cca_mode = mrf24j40_set_cca_mode, + .set_cca_ed_level = mrf24j40_set_cca_ed_level, + .set_txpower = mrf24j40_set_txpower, + .set_promiscuous_mode = mrf24j40_set_promiscuous_mode, }; -static irqreturn_t mrf24j40_isr(int irq, void *data) +static void mrf24j40_intstat_complete(void *context) { - struct mrf24j40 *devrec = data; - u8 intstat; - int ret; + struct mrf24j40 *devrec = context; + u8 intstat = devrec->irq_buf[1]; - /* Read the interrupt status */ - ret = read_short_reg(devrec, REG_INTSTAT, &intstat); - if (ret) - goto out; + enable_irq(devrec->spi->irq); /* Check for TX complete */ - if (intstat & 0x1) - complete(&devrec->tx_complete); + if (intstat & BIT_TXNIF) + ieee802154_xmit_complete(devrec->hw, devrec->tx_skb, false); /* Check for Rx */ - if (intstat & 0x8) + if (intstat & BIT_RXIF) mrf24j40_handle_rx(devrec); +} + +static irqreturn_t mrf24j40_isr(int irq, void *data) +{ + struct mrf24j40 *devrec = data; + int ret; + + disable_irq_nosync(irq); + + devrec->irq_buf[0] = MRF24J40_READSHORT(REG_INTSTAT); + /* Read the interrupt status */ + ret = spi_async(devrec->spi, &devrec->irq_msg); + if (ret) { + enable_irq(irq); + return IRQ_NONE; + } -out: return IRQ_HANDLED; } static int mrf24j40_hw_init(struct mrf24j40 *devrec) { + u32 irq_type; int ret; - u8 val; /* Initialize the device. From datasheet section 3.2: Initialization. */ - ret = write_short_reg(devrec, REG_SOFTRST, 0x07); + ret = regmap_write(devrec->regmap_short, REG_SOFTRST, 0x07); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_PACON2, 0x98); + ret = regmap_write(devrec->regmap_short, REG_PACON2, 0x98); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_TXSTBL, 0x95); + ret = regmap_write(devrec->regmap_short, REG_TXSTBL, 0x95); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON0, 0x03); + ret = regmap_write(devrec->regmap_long, REG_RFCON0, 0x03); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON1, 0x01); + ret = regmap_write(devrec->regmap_long, REG_RFCON1, 0x01); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON2, 0x80); + ret = regmap_write(devrec->regmap_long, REG_RFCON2, 0x80); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON6, 0x90); + ret = regmap_write(devrec->regmap_long, REG_RFCON6, 0x90); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON7, 0x80); + ret = regmap_write(devrec->regmap_long, REG_RFCON7, 0x80); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_RFCON8, 0x10); + ret = regmap_write(devrec->regmap_long, REG_RFCON8, 0x10); if (ret) goto err_ret; - ret = write_long_reg(devrec, REG_SLPCON1, 0x21); + ret = regmap_write(devrec->regmap_long, REG_SLPCON1, 0x21); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_BBREG2, 0x80); + ret = regmap_write(devrec->regmap_short, REG_BBREG2, 0x80); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_CCAEDTH, 0x60); + ret = regmap_write(devrec->regmap_short, REG_CCAEDTH, 0x60); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_BBREG6, 0x40); + ret = regmap_write(devrec->regmap_short, REG_BBREG6, 0x40); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_RFCTL, 0x04); + ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x04); if (ret) goto err_ret; - ret = write_short_reg(devrec, REG_RFCTL, 0x0); + ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x0); if (ret) goto err_ret; udelay(192); /* Set RX Mode. RXMCR<1:0>: 0x0 normal, 0x1 promisc, 0x2 error */ - ret = read_short_reg(devrec, REG_RXMCR, &val); - if (ret) - goto err_ret; - - val &= ~0x3; /* Clear RX mode (normal) */ - - ret = write_short_reg(devrec, REG_RXMCR, val); + ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x03, 0x00); if (ret) goto err_ret; @@ -696,22 +1129,39 @@ static int mrf24j40_hw_init(struct mrf24j40 *devrec) /* Enable external amplifier. * From MRF24J40MC datasheet section 1.3: Operation. */ - read_long_reg(devrec, REG_TESTMODE, &val); - val |= 0x7; /* Configure GPIO 0-2 to control amplifier */ - write_long_reg(devrec, REG_TESTMODE, val); + regmap_update_bits(devrec->regmap_long, REG_TESTMODE, 0x07, + 0x07); - read_short_reg(devrec, REG_TRISGPIO, &val); - val |= 0x8; /* Set GPIO3 as output. */ - write_short_reg(devrec, REG_TRISGPIO, val); + /* Set GPIO3 as output. */ + regmap_update_bits(devrec->regmap_short, REG_TRISGPIO, 0x08, + 0x08); - read_short_reg(devrec, REG_GPIO, &val); - val |= 0x8; /* Set GPIO3 HIGH to enable U5 voltage regulator */ - write_short_reg(devrec, REG_GPIO, val); + /* Set GPIO3 HIGH to enable U5 voltage regulator */ + regmap_update_bits(devrec->regmap_short, REG_GPIO, 0x08, 0x08); /* Reduce TX pwr to meet FCC requirements. * From MRF24J40MC datasheet section 3.1.1 */ - write_long_reg(devrec, REG_RFCON3, 0x28); + regmap_write(devrec->regmap_long, REG_RFCON3, 0x28); + } + + irq_type = irq_get_trigger_type(devrec->spi->irq); + if (irq_type == IRQ_TYPE_EDGE_RISING || + irq_type == IRQ_TYPE_EDGE_FALLING) + dev_warn(&devrec->spi->dev, + "Using edge triggered irq's are not recommended, because it can cause races and result in a non-functional driver!\n"); + switch (irq_type) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_LEVEL_HIGH: + /* set interrupt polarity to rising */ + ret = regmap_update_bits(devrec->regmap_long, REG_SLPCON0, + BIT_INTEDGE, BIT_INTEDGE); + if (ret) + goto err_ret; + break; + default: + /* default is falling edge */ + break; } return 0; @@ -720,67 +1170,178 @@ err_ret: return ret; } -static int mrf24j40_probe(struct spi_device *spi) +static void +mrf24j40_setup_tx_spi_messages(struct mrf24j40 *devrec) { - int ret = -ENOMEM; - struct mrf24j40 *devrec; + spi_message_init(&devrec->tx_msg); + devrec->tx_msg.context = devrec; + devrec->tx_msg.complete = write_tx_buf_complete; + devrec->tx_hdr_trx.len = 2; + devrec->tx_hdr_trx.tx_buf = devrec->tx_hdr_buf; + spi_message_add_tail(&devrec->tx_hdr_trx, &devrec->tx_msg); + devrec->tx_len_trx.len = 2; + devrec->tx_len_trx.tx_buf = devrec->tx_len_buf; + spi_message_add_tail(&devrec->tx_len_trx, &devrec->tx_msg); + spi_message_add_tail(&devrec->tx_buf_trx, &devrec->tx_msg); + + spi_message_init(&devrec->tx_post_msg); + devrec->tx_post_msg.context = devrec; + devrec->tx_post_trx.len = 2; + devrec->tx_post_trx.tx_buf = devrec->tx_post_buf; + spi_message_add_tail(&devrec->tx_post_trx, &devrec->tx_post_msg); +} - dev_info(&spi->dev, "probe(). IRQ: %d\n", spi->irq); +static void +mrf24j40_setup_rx_spi_messages(struct mrf24j40 *devrec) +{ + spi_message_init(&devrec->rx_msg); + devrec->rx_msg.context = devrec; + devrec->rx_trx.len = 2; + devrec->rx_trx.tx_buf = devrec->rx_buf; + devrec->rx_trx.rx_buf = devrec->rx_buf; + spi_message_add_tail(&devrec->rx_trx, &devrec->rx_msg); + + spi_message_init(&devrec->rx_buf_msg); + devrec->rx_buf_msg.context = devrec; + devrec->rx_buf_msg.complete = mrf24j40_handle_rx_read_buf_complete; + devrec->rx_addr_trx.len = 2; + devrec->rx_addr_trx.tx_buf = devrec->rx_addr_buf; + spi_message_add_tail(&devrec->rx_addr_trx, &devrec->rx_buf_msg); + devrec->rx_fifo_buf_trx.rx_buf = devrec->rx_fifo_buf; + spi_message_add_tail(&devrec->rx_fifo_buf_trx, &devrec->rx_buf_msg); + devrec->rx_lqi_trx.len = 2; + devrec->rx_lqi_trx.rx_buf = devrec->rx_lqi_buf; + spi_message_add_tail(&devrec->rx_lqi_trx, &devrec->rx_buf_msg); +} - devrec = devm_kzalloc(&spi->dev, sizeof(struct mrf24j40), GFP_KERNEL); - if (!devrec) - goto err_ret; - devrec->buf = devm_kzalloc(&spi->dev, 3, GFP_KERNEL); - if (!devrec->buf) - goto err_ret; +static void +mrf24j40_setup_irq_spi_messages(struct mrf24j40 *devrec) +{ + spi_message_init(&devrec->irq_msg); + devrec->irq_msg.context = devrec; + devrec->irq_msg.complete = mrf24j40_intstat_complete; + devrec->irq_trx.len = 2; + devrec->irq_trx.tx_buf = devrec->irq_buf; + devrec->irq_trx.rx_buf = devrec->irq_buf; + spi_message_add_tail(&devrec->irq_trx, &devrec->irq_msg); +} + +static void mrf24j40_phy_setup(struct mrf24j40 *devrec) +{ + ieee802154_random_extended_addr(&devrec->hw->phy->perm_extended_addr); + devrec->hw->phy->current_channel = 11; + + /* mrf24j40 supports max_minbe 0 - 3 */ + devrec->hw->phy->supported.max_minbe = 3; + /* datasheet doesn't say anything about max_be, but we have min_be + * So we assume the max_be default. + */ + devrec->hw->phy->supported.min_maxbe = 5; + devrec->hw->phy->supported.max_maxbe = 5; + + devrec->hw->phy->cca.mode = NL802154_CCA_CARRIER; + devrec->hw->phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY) | + BIT(NL802154_CCA_CARRIER) | + BIT(NL802154_CCA_ENERGY_CARRIER); + devrec->hw->phy->supported.cca_opts = BIT(NL802154_CCA_OPT_ENERGY_CARRIER_AND); + + devrec->hw->phy->cca_ed_level = -6900; + devrec->hw->phy->supported.cca_ed_levels = mrf24j40_ed_levels; + devrec->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(mrf24j40_ed_levels); + + switch (spi_get_device_id(devrec->spi)->driver_data) { + case MRF24J40: + case MRF24J40MA: + devrec->hw->phy->supported.tx_powers = mrf24j40ma_powers; + devrec->hw->phy->supported.tx_powers_size = ARRAY_SIZE(mrf24j40ma_powers); + devrec->hw->phy->flags |= WPAN_PHY_FLAG_TXPOWER; + break; + default: + break; + } +} - spi->mode = SPI_MODE_0; /* TODO: Is this appropriate for right here? */ - if (spi->max_speed_hz > MAX_SPI_SPEED_HZ) - spi->max_speed_hz = MAX_SPI_SPEED_HZ; +static int mrf24j40_probe(struct spi_device *spi) +{ + int ret = -ENOMEM, irq_type; + struct ieee802154_hw *hw; + struct mrf24j40 *devrec; - mutex_init(&devrec->buffer_mutex); - init_completion(&devrec->tx_complete); - devrec->spi = spi; - spi_set_drvdata(spi, devrec); + dev_info(&spi->dev, "probe(). IRQ: %d\n", spi->irq); /* Register with the 802154 subsystem */ - devrec->hw = ieee802154_alloc_hw(0, &mrf24j40_ops); - if (!devrec->hw) + hw = ieee802154_alloc_hw(sizeof(*devrec), &mrf24j40_ops); + if (!hw) goto err_ret; - devrec->hw->priv = devrec; - devrec->hw->parent = &devrec->spi->dev; + devrec = hw->priv; + devrec->spi = spi; + spi_set_drvdata(spi, devrec); + devrec->hw = hw; + devrec->hw->parent = &spi->dev; devrec->hw->phy->supported.channels[0] = CHANNEL_MASK; - devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT; + devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | + IEEE802154_HW_CSMA_PARAMS | + IEEE802154_HW_PROMISCUOUS; + + devrec->hw->phy->flags = WPAN_PHY_FLAG_CCA_MODE | + WPAN_PHY_FLAG_CCA_ED_LEVEL; + + mrf24j40_setup_tx_spi_messages(devrec); + mrf24j40_setup_rx_spi_messages(devrec); + mrf24j40_setup_irq_spi_messages(devrec); + + devrec->regmap_short = devm_regmap_init_spi(spi, + &mrf24j40_short_regmap); + if (IS_ERR(devrec->regmap_short)) { + ret = PTR_ERR(devrec->regmap_short); + dev_err(&spi->dev, "Failed to allocate short register map: %d\n", + ret); + goto err_register_device; + } - dev_dbg(printdev(devrec), "registered mrf24j40\n"); - ret = ieee802154_register_hw(devrec->hw); - if (ret) + devrec->regmap_long = devm_regmap_init(&spi->dev, + &mrf24j40_long_regmap_bus, + spi, &mrf24j40_long_regmap); + if (IS_ERR(devrec->regmap_long)) { + ret = PTR_ERR(devrec->regmap_long); + dev_err(&spi->dev, "Failed to allocate long register map: %d\n", + ret); goto err_register_device; + } + + if (spi->max_speed_hz > MAX_SPI_SPEED_HZ) { + dev_warn(&spi->dev, "spi clock above possible maximum: %d", + MAX_SPI_SPEED_HZ); + return -EINVAL; + } ret = mrf24j40_hw_init(devrec); if (ret) - goto err_hw_init; + goto err_register_device; - ret = devm_request_threaded_irq(&spi->dev, - spi->irq, - NULL, - mrf24j40_isr, - IRQF_TRIGGER_LOW|IRQF_ONESHOT, - dev_name(&spi->dev), - devrec); + mrf24j40_phy_setup(devrec); + /* request IRQF_TRIGGER_LOW as fallback default */ + irq_type = irq_get_trigger_type(spi->irq); + if (!irq_type) + irq_type = IRQF_TRIGGER_LOW; + + ret = devm_request_irq(&spi->dev, spi->irq, mrf24j40_isr, + irq_type, dev_name(&spi->dev), devrec); if (ret) { dev_err(printdev(devrec), "Unable to get IRQ"); - goto err_irq; + goto err_register_device; } + dev_dbg(printdev(devrec), "registered mrf24j40\n"); + ret = ieee802154_register_hw(devrec->hw); + if (ret) + goto err_register_device; + return 0; -err_irq: -err_hw_init: - ieee802154_unregister_hw(devrec->hw); err_register_device: ieee802154_free_hw(devrec->hw); err_ret: @@ -801,6 +1362,14 @@ static int mrf24j40_remove(struct spi_device *spi) return 0; } +static const struct of_device_id mrf24j40_of_match[] = { + { .compatible = "microchip,mrf24j40", .data = (void *)MRF24J40 }, + { .compatible = "microchip,mrf24j40ma", .data = (void *)MRF24J40MA }, + { .compatible = "microchip,mrf24j40mc", .data = (void *)MRF24J40MC }, + { }, +}; +MODULE_DEVICE_TABLE(of, mrf24j40_of_match); + static const struct spi_device_id mrf24j40_ids[] = { { "mrf24j40", MRF24J40 }, { "mrf24j40ma", MRF24J40MA }, @@ -811,6 +1380,7 @@ MODULE_DEVICE_TABLE(spi, mrf24j40_ids); static struct spi_driver mrf24j40_driver = { .driver = { + .of_match_table = of_match_ptr(mrf24j40_of_match), .name = "mrf24j40", .owner = THIS_MODULE, }, diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 207f62e8de9a..24f8dbcf854f 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -344,6 +344,7 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb) { const struct iphdr *ip4h = ip_hdr(skb); struct net_device *dev = skb->dev; + struct net *net = dev_net(dev); struct rtable *rt; int err, ret = NET_XMIT_DROP; struct flowi4 fl4 = { @@ -354,7 +355,7 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb) .saddr = ip4h->saddr, }; - rt = ip_route_output_flow(dev_net(dev), &fl4, NULL); + rt = ip_route_output_flow(net, &fl4, NULL); if (IS_ERR(rt)) goto err; @@ -364,7 +365,7 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb) } skb_dst_drop(skb); skb_dst_set(skb, &rt->dst); - err = ip_local_out(skb); + err = ip_local_out(net, skb->sk, skb); if (unlikely(net_xmit_eval(err))) dev->stats.tx_errors++; else @@ -381,6 +382,7 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb) { const struct ipv6hdr *ip6h = ipv6_hdr(skb); struct net_device *dev = skb->dev; + struct net *net = dev_net(dev); struct dst_entry *dst; int err, ret = NET_XMIT_DROP; struct flowi6 fl6 = { @@ -393,7 +395,7 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb) .flowi6_proto = ip6h->nexthdr, }; - dst = ip6_route_output(dev_net(dev), NULL, &fl6); + dst = ip6_route_output(net, NULL, &fl6); if (dst->error) { ret = dst->error; dst_release(dst); @@ -401,7 +403,7 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb) } skb_dst_drop(skb); skb_dst_set(skb, dst); - err = ip6_local_out(skb); + err = ip6_local_out(net, skb->sk, skb); if (unlikely(net_xmit_eval(err))) dev->stats.tx_errors++; else diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index 58ae11a14bb6..64bb44d5d867 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -1031,7 +1031,6 @@ static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 baud) static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed) { struct ali_ircc_cb *self = priv; - unsigned long flags; int iobase; int fcr; /* FIFO control reg */ int lcr; /* Line control reg */ @@ -1061,8 +1060,6 @@ static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed) /* Update accounting for new speed */ self->io.speed = speed; - spin_lock_irqsave(&self->lock, flags); - divisor = 115200/speed; fcr = UART_FCR_ENABLE_FIFO; @@ -1089,9 +1086,6 @@ static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed) /* without this, the connection will be broken after come back from FIR speed, but with this, the SIR connection is harder to established */ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR); - - spin_unlock_irqrestore(&self->lock, flags); - } static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed) diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c index 100454662e4b..6e8f616be48e 100644 --- a/drivers/net/irda/pxaficp_ir.c +++ b/drivers/net/irda/pxaficp_ir.c @@ -19,6 +19,9 @@ #include <linux/etherdevice.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/dma/pxa-dma.h> #include <linux/gpio.h> #include <linux/slab.h> @@ -27,18 +30,17 @@ #include <net/irda/wrapper.h> #include <net/irda/irda_device.h> -#include <mach/dma.h> #include <linux/platform_data/irda-pxaficp.h> -#include <mach/regs-ost.h> +#undef __REG +#define __REG(x) ((x) & 0xffff) #include <mach/regs-uart.h> -#define FICP __REG(0x40800000) /* Start of FICP area */ -#define ICCR0 __REG(0x40800000) /* ICP Control Register 0 */ -#define ICCR1 __REG(0x40800004) /* ICP Control Register 1 */ -#define ICCR2 __REG(0x40800008) /* ICP Control Register 2 */ -#define ICDR __REG(0x4080000c) /* ICP Data Register */ -#define ICSR0 __REG(0x40800014) /* ICP Status Register 0 */ -#define ICSR1 __REG(0x40800018) /* ICP Status Register 1 */ +#define ICCR0 0x0000 /* ICP Control Register 0 */ +#define ICCR1 0x0004 /* ICP Control Register 1 */ +#define ICCR2 0x0008 /* ICP Control Register 2 */ +#define ICDR 0x000c /* ICP Data Register */ +#define ICSR0 0x0014 /* ICP Status Register 0 */ +#define ICSR1 0x0018 /* ICP Status Register 1 */ #define ICCR0_AME (1 << 7) /* Address match enable */ #define ICCR0_TIE (1 << 6) /* Transmit FIFO interrupt enable */ @@ -56,9 +58,7 @@ #define ICCR2_TRIG_16 (1 << 0) /* >= 16 bytes */ #define ICCR2_TRIG_32 (2 << 0) /* >= 32 bytes */ -#ifdef CONFIG_PXA27x #define ICSR0_EOC (1 << 6) /* DMA End of Descriptor Chain */ -#endif #define ICSR0_FRE (1 << 5) /* Framing error */ #define ICSR0_RFS (1 << 4) /* Receive FIFO service request */ #define ICSR0_TFS (1 << 3) /* Transnit FIFO service request */ @@ -99,18 +99,61 @@ IrSR_RCVEIR_UART_MODE | \ IrSR_XMITIR_IR_MODE) +/* macros for registers read/write */ +#define ficp_writel(irda, val, off) \ + do { \ + dev_vdbg(irda->dev, \ + "%s():%d ficp_writel(0x%x, %s)\n", \ + __func__, __LINE__, (val), #off); \ + writel_relaxed((val), (irda)->irda_base + (off)); \ + } while (0) + +#define ficp_readl(irda, off) \ + ({ \ + unsigned int _v; \ + _v = readl_relaxed((irda)->irda_base + (off)); \ + dev_vdbg(irda->dev, \ + "%s():%d ficp_readl(%s): 0x%x\n", \ + __func__, __LINE__, #off, _v); \ + _v; \ + }) + +#define stuart_writel(irda, val, off) \ + do { \ + dev_vdbg(irda->dev, \ + "%s():%d stuart_writel(0x%x, %s)\n", \ + __func__, __LINE__, (val), #off); \ + writel_relaxed((val), (irda)->stuart_base + (off)); \ + } while (0) + +#define stuart_readl(irda, off) \ + ({ \ + unsigned int _v; \ + _v = readl_relaxed((irda)->stuart_base + (off)); \ + dev_vdbg(irda->dev, \ + "%s():%d stuart_readl(%s): 0x%x\n", \ + __func__, __LINE__, #off, _v); \ + _v; \ + }) + struct pxa_irda { int speed; int newspeed; - unsigned long last_oscr; + unsigned long long last_clk; + void __iomem *stuart_base; + void __iomem *irda_base; unsigned char *dma_rx_buff; unsigned char *dma_tx_buff; dma_addr_t dma_rx_buff_phy; dma_addr_t dma_tx_buff_phy; unsigned int dma_tx_buff_len; - int txdma; - int rxdma; + struct dma_chan *txdma; + struct dma_chan *rxdma; + dma_cookie_t rx_cookie; + dma_cookie_t tx_cookie; + int drcmr_rx; + int drcmr_tx; int uart_irq; int icp_irq; @@ -128,6 +171,8 @@ struct pxa_irda { struct clk *cur_clk; }; +static int pxa_irda_set_speed(struct pxa_irda *si, int speed); + static inline void pxa_irda_disable_clk(struct pxa_irda *si) { if (si->cur_clk) @@ -151,22 +196,41 @@ static inline void pxa_irda_enable_sirclk(struct pxa_irda *si) #define IS_FIR(si) ((si)->speed >= 4000000) #define IRDA_FRAME_SIZE_LIMIT 2047 +static void pxa_irda_fir_dma_rx_irq(void *data); +static void pxa_irda_fir_dma_tx_irq(void *data); + inline static void pxa_irda_fir_dma_rx_start(struct pxa_irda *si) { - DCSR(si->rxdma) = DCSR_NODESC; - DSADR(si->rxdma) = __PREG(ICDR); - DTADR(si->rxdma) = si->dma_rx_buff_phy; - DCMD(si->rxdma) = DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_WIDTH1 | DCMD_BURST32 | IRDA_FRAME_SIZE_LIMIT; - DCSR(si->rxdma) |= DCSR_RUN; + struct dma_async_tx_descriptor *tx; + + tx = dmaengine_prep_slave_single(si->rxdma, si->dma_rx_buff_phy, + IRDA_FRAME_SIZE_LIMIT, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(si->dev, "prep_slave_sg() failed\n"); + return; + } + tx->callback = pxa_irda_fir_dma_rx_irq; + tx->callback_param = si; + si->rx_cookie = dmaengine_submit(tx); + dma_async_issue_pending(si->rxdma); } inline static void pxa_irda_fir_dma_tx_start(struct pxa_irda *si) { - DCSR(si->txdma) = DCSR_NODESC; - DSADR(si->txdma) = si->dma_tx_buff_phy; - DTADR(si->txdma) = __PREG(ICDR); - DCMD(si->txdma) = DCMD_INCSRCADDR | DCMD_FLOWTRG | DCMD_ENDIRQEN | DCMD_WIDTH1 | DCMD_BURST32 | si->dma_tx_buff_len; - DCSR(si->txdma) |= DCSR_RUN; + struct dma_async_tx_descriptor *tx; + + tx = dmaengine_prep_slave_single(si->txdma, si->dma_tx_buff_phy, + si->dma_tx_buff_len, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(si->dev, "prep_slave_sg() failed\n"); + return; + } + tx->callback = pxa_irda_fir_dma_tx_irq; + tx->callback_param = si; + si->tx_cookie = dmaengine_submit(tx); + dma_async_issue_pending(si->rxdma); } /* @@ -205,9 +269,9 @@ static int pxa_irda_set_speed(struct pxa_irda *si, int speed) if (IS_FIR(si)) { /* stop RX DMA */ - DCSR(si->rxdma) &= ~DCSR_RUN; + dmaengine_terminate_all(si->rxdma); /* disable FICP */ - ICCR0 = 0; + ficp_writel(si, 0, ICCR0); pxa_irda_disable_clk(si); /* set board transceiver to SIR mode */ @@ -218,17 +282,19 @@ static int pxa_irda_set_speed(struct pxa_irda *si, int speed) } /* disable STUART first */ - STIER = 0; + stuart_writel(si, 0, STIER); /* access DLL & DLH */ - STLCR |= LCR_DLAB; - STDLL = divisor & 0xff; - STDLH = divisor >> 8; - STLCR &= ~LCR_DLAB; + stuart_writel(si, stuart_readl(si, STLCR) | LCR_DLAB, STLCR); + stuart_writel(si, divisor & 0xff, STDLL); + stuart_writel(si, divisor >> 8, STDLH); + stuart_writel(si, stuart_readl(si, STLCR) & ~LCR_DLAB, STLCR); si->speed = speed; - STISR = IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6; - STIER = IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE; + stuart_writel(si, IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6, + STISR); + stuart_writel(si, IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE, + STIER); local_irq_restore(flags); break; @@ -237,12 +303,12 @@ static int pxa_irda_set_speed(struct pxa_irda *si, int speed) local_irq_save(flags); /* disable STUART */ - STIER = 0; - STISR = 0; + stuart_writel(si, 0, STIER); + stuart_writel(si, 0, STISR); pxa_irda_disable_clk(si); /* disable FICP first */ - ICCR0 = 0; + ficp_writel(si, 0, ICCR0); /* set board transceiver to FIR mode */ pxa_irda_set_mode(si, IR_FIRMODE); @@ -252,7 +318,7 @@ static int pxa_irda_set_speed(struct pxa_irda *si, int speed) si->speed = speed; pxa_irda_fir_dma_rx_start(si); - ICCR0 = ICCR0_ITR | ICCR0_RXE; + ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); local_irq_restore(flags); break; @@ -271,13 +337,13 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) struct pxa_irda *si = netdev_priv(dev); int iir, lsr, data; - iir = STIIR; + iir = stuart_readl(si, STIIR); switch (iir & 0x0F) { case 0x06: /* Receiver Line Status */ - lsr = STLSR; + lsr = stuart_readl(si, STLSR); while (lsr & LSR_FIFOE) { - data = STRBR; + data = stuart_readl(si, STRBR); if (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) { printk(KERN_DEBUG "pxa_ir: sir receiving error\n"); dev->stats.rx_errors++; @@ -290,9 +356,9 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) async_unwrap_char(dev, &dev->stats, &si->rx_buff, data); } - lsr = STLSR; + lsr = stuart_readl(si, STLSR); } - si->last_oscr = readl_relaxed(OSCR); + si->last_clk = sched_clock(); break; case 0x04: /* Received Data Available */ @@ -301,14 +367,16 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) case 0x0C: /* Character Timeout Indication */ do { dev->stats.rx_bytes++; - async_unwrap_char(dev, &dev->stats, &si->rx_buff, STRBR); - } while (STLSR & LSR_DR); - si->last_oscr = readl_relaxed(OSCR); + async_unwrap_char(dev, &dev->stats, &si->rx_buff, + stuart_readl(si, STRBR)); + } while (stuart_readl(si, STLSR) & LSR_DR); + si->last_clk = sched_clock(); break; case 0x02: /* Transmit FIFO Data Request */ - while ((si->tx_buff.len) && (STLSR & LSR_TDRQ)) { - STTHR = *si->tx_buff.data++; + while ((si->tx_buff.len) && + (stuart_readl(si, STLSR) & LSR_TDRQ)) { + stuart_writel(si, *si->tx_buff.data++, STTHR); si->tx_buff.len -= 1; } @@ -317,9 +385,9 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) dev->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head; /* We need to ensure that the transmitter has finished. */ - while ((STLSR & LSR_TEMT) == 0) + while ((stuart_readl(si, STLSR) & LSR_TEMT) == 0) cpu_relax(); - si->last_oscr = readl_relaxed(OSCR); + si->last_clk = sched_clock(); /* * Ok, we've finished transmitting. Now enable @@ -331,9 +399,11 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) si->newspeed = 0; } else { /* enable IR Receiver, disable IR Transmitter */ - STISR = IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6; + stuart_writel(si, IrSR_IR_RECEIVE_ON | + IrSR_XMODE_PULSE_1_6, STISR); /* enable STUART and receive interrupts */ - STIER = IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE; + stuart_writel(si, IER_UUE | IER_RLSE | + IER_RAVIE | IER_RTIOE, STIER); } /* I'm hungry! */ netif_wake_queue(dev); @@ -345,35 +415,32 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) } /* FIR Receive DMA interrupt handler */ -static void pxa_irda_fir_dma_rx_irq(int channel, void *data) +static void pxa_irda_fir_dma_rx_irq(void *data) { - int dcsr = DCSR(channel); - - DCSR(channel) = dcsr & ~DCSR_RUN; + struct net_device *dev = data; + struct pxa_irda *si = netdev_priv(dev); - printk(KERN_DEBUG "pxa_ir: fir rx dma bus error %#x\n", dcsr); + dmaengine_terminate_all(si->rxdma); + netdev_dbg(dev, "pxa_ir: fir rx dma bus error\n"); } /* FIR Transmit DMA interrupt handler */ -static void pxa_irda_fir_dma_tx_irq(int channel, void *data) +static void pxa_irda_fir_dma_tx_irq(void *data) { struct net_device *dev = data; struct pxa_irda *si = netdev_priv(dev); - int dcsr; - - dcsr = DCSR(channel); - DCSR(channel) = dcsr & ~DCSR_RUN; - if (dcsr & DCSR_ENDINTR) { + dmaengine_terminate_all(si->txdma); + if (dmaengine_tx_status(si->txdma, si->tx_cookie, NULL) == DMA_ERROR) { + dev->stats.tx_errors++; + } else { dev->stats.tx_packets++; dev->stats.tx_bytes += si->dma_tx_buff_len; - } else { - dev->stats.tx_errors++; } - while (ICSR1 & ICSR1_TBY) + while (ficp_readl(si, ICSR1) & ICSR1_TBY) cpu_relax(); - si->last_oscr = readl_relaxed(OSCR); + si->last_clk = sched_clock(); /* * HACK: It looks like the TBY bit is dropped too soon. @@ -387,11 +454,11 @@ static void pxa_irda_fir_dma_tx_irq(int channel, void *data) } else { int i = 64; - ICCR0 = 0; + ficp_writel(si, 0, ICCR0); pxa_irda_fir_dma_rx_start(si); - while ((ICSR1 & ICSR1_RNE) && i--) - (void)ICDR; - ICCR0 = ICCR0_ITR | ICCR0_RXE; + while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--) + ficp_readl(si, ICDR); + ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); if (i < 0) printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n"); @@ -403,15 +470,18 @@ static void pxa_irda_fir_dma_tx_irq(int channel, void *data) static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, int icsr0) { unsigned int len, stat, data; + struct dma_tx_state state; /* Get the current data position. */ - len = DTADR(si->rxdma) - si->dma_rx_buff_phy; + + dmaengine_tx_status(si->rxdma, si->rx_cookie, &state); + len = IRDA_FRAME_SIZE_LIMIT - state.residue; do { /* Read Status, and then Data. */ - stat = ICSR1; + stat = ficp_readl(si, ICSR1); rmb(); - data = ICDR; + data = ficp_readl(si, ICDR); if (stat & (ICSR1_CRE | ICSR1_ROR)) { dev->stats.rx_errors++; @@ -429,7 +499,7 @@ static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, in /* If we hit the end of frame, there's no point in continuing. */ if (stat & ICSR1_EOF) break; - } while (ICSR0 & ICSR0_EIF); + } while (ficp_readl(si, ICSR0) & ICSR0_EIF); if (stat & ICSR1_EOF) { /* end of frame. */ @@ -472,9 +542,9 @@ static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id) int icsr0, i = 64; /* stop RX DMA */ - DCSR(si->rxdma) &= ~DCSR_RUN; - si->last_oscr = readl_relaxed(OSCR); - icsr0 = ICSR0; + dmaengine_terminate_all(si->rxdma); + si->last_clk = sched_clock(); + icsr0 = ficp_readl(si, ICSR0); if (icsr0 & (ICSR0_FRE | ICSR0_RAB)) { if (icsr0 & ICSR0_FRE) { @@ -484,7 +554,7 @@ static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id) printk(KERN_DEBUG "pxa_ir: fir receive abort\n"); dev->stats.rx_errors++; } - ICSR0 = icsr0 & (ICSR0_FRE | ICSR0_RAB); + ficp_writel(si, icsr0 & (ICSR0_FRE | ICSR0_RAB), ICSR0); } if (icsr0 & ICSR0_EIF) { @@ -492,11 +562,11 @@ static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id) pxa_irda_fir_irq_eif(si, dev, icsr0); } - ICCR0 = 0; + ficp_writel(si, 0, ICCR0); pxa_irda_fir_dma_rx_start(si); - while ((ICSR1 & ICSR1_RNE) && i--) - (void)ICDR; - ICCR0 = ICCR0_ITR | ICCR0_RXE; + while ((ficp_readl(si, ICSR1) & ICSR1_RNE) && i--) + ficp_readl(si, ICDR); + ficp_writel(si, ICCR0_ITR | ICCR0_RXE, ICCR0); if (i < 0) printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n"); @@ -537,11 +607,12 @@ static int pxa_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, si->tx_buff.truesize); /* Disable STUART interrupts and switch to transmit mode. */ - STIER = 0; - STISR = IrSR_IR_TRANSMIT_ON | IrSR_XMODE_PULSE_1_6; + stuart_writel(si, 0, STIER); + stuart_writel(si, IrSR_IR_TRANSMIT_ON | IrSR_XMODE_PULSE_1_6, + STISR); /* enable STUART and transmit interrupts */ - STIER = IER_UUE | IER_TIE; + stuart_writel(si, IER_UUE | IER_TIE, STIER); } else { unsigned long mtt = irda_get_mtt(skb); @@ -549,15 +620,15 @@ static int pxa_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) skb_copy_from_linear_data(skb, si->dma_tx_buff, skb->len); if (mtt) - while ((unsigned)(readl_relaxed(OSCR) - si->last_oscr)/4 < mtt) + while ((sched_clock() - si->last_clk) * 1000 < mtt) cpu_relax(); /* stop RX DMA, disable FICP */ - DCSR(si->rxdma) &= ~DCSR_RUN; - ICCR0 = 0; + dmaengine_terminate_all(si->rxdma); + ficp_writel(si, 0, ICCR0); pxa_irda_fir_dma_tx_start(si); - ICCR0 = ICCR0_ITR | ICCR0_TXE; + ficp_writel(si, ICCR0_ITR | ICCR0_TXE, ICCR0); } dev_kfree_skb(skb); @@ -613,22 +684,18 @@ static int pxa_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) static void pxa_irda_startup(struct pxa_irda *si) { /* Disable STUART interrupts */ - STIER = 0; + stuart_writel(si, 0, STIER); /* enable STUART interrupt to the processor */ - STMCR = MCR_OUT2; + stuart_writel(si, MCR_OUT2, STMCR); /* configure SIR frame format: StartBit - Data 7 ... Data 0 - Stop Bit */ - STLCR = LCR_WLS0 | LCR_WLS1; + stuart_writel(si, LCR_WLS0 | LCR_WLS1, STLCR); /* enable FIFO, we use FIFO to improve performance */ - STFCR = FCR_TRFIFOE | FCR_ITL_32; + stuart_writel(si, FCR_TRFIFOE | FCR_ITL_32, STFCR); /* disable FICP */ - ICCR0 = 0; + ficp_writel(si, 0, ICCR0); /* configure FICP ICCR2 */ - ICCR2 = ICCR2_TXP | ICCR2_TRIG_32; - - /* configure DMAC */ - DRCMR(17) = si->rxdma | DRCMR_MAPVLD; - DRCMR(18) = si->txdma | DRCMR_MAPVLD; + ficp_writel(si, ICCR2_TXP | ICCR2_TRIG_32, ICCR2); /* force SIR reinitialization */ si->speed = 4000000; @@ -644,22 +711,19 @@ static void pxa_irda_shutdown(struct pxa_irda *si) local_irq_save(flags); /* disable STUART and interrupt */ - STIER = 0; + stuart_writel(si, 0, STIER); /* disable STUART SIR mode */ - STISR = 0; + stuart_writel(si, 0, STISR); /* disable DMA */ - DCSR(si->txdma) &= ~DCSR_RUN; - DCSR(si->rxdma) &= ~DCSR_RUN; + dmaengine_terminate_all(si->rxdma); + dmaengine_terminate_all(si->txdma); /* disable FICP */ - ICCR0 = 0; + ficp_writel(si, 0, ICCR0); /* disable the STUART or FICP clocks */ pxa_irda_disable_clk(si); - DRCMR(17) = 0; - DRCMR(18) = 0; - local_irq_restore(flags); /* power off board transceiver */ @@ -671,6 +735,9 @@ static void pxa_irda_shutdown(struct pxa_irda *si) static int pxa_irda_start(struct net_device *dev) { struct pxa_irda *si = netdev_priv(dev); + dma_cap_mask_t mask; + struct dma_slave_config config; + struct pxad_param param; int err; si->speed = 9600; @@ -690,14 +757,37 @@ static int pxa_irda_start(struct net_device *dev) disable_irq(si->icp_irq); err = -EBUSY; - si->rxdma = pxa_request_dma("FICP_RX",DMA_PRIO_LOW, pxa_irda_fir_dma_rx_irq, dev); - if (si->rxdma < 0) + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + param.prio = PXAD_PRIO_LOWEST; + + memset(&config, 0, sizeof(config)); + config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.src_addr = (dma_addr_t)si->irda_base + ICDR; + config.dst_addr = (dma_addr_t)si->irda_base + ICDR; + config.src_maxburst = 32; + config.dst_maxburst = 32; + + param.drcmr = si->drcmr_rx; + si->rxdma = dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m, &dev->dev, "rx"); + if (!si->rxdma) goto err_rx_dma; - si->txdma = pxa_request_dma("FICP_TX",DMA_PRIO_LOW, pxa_irda_fir_dma_tx_irq, dev); - if (si->txdma < 0) + param.drcmr = si->drcmr_tx; + si->txdma = dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m, &dev->dev, "tx"); + if (!si->txdma) goto err_tx_dma; + err = dmaengine_slave_config(si->rxdma, &config); + if (err) + goto err_dma_rx_buff; + err = dmaengine_slave_config(si->txdma, &config); + if (err) + goto err_dma_rx_buff; + err = -ENOMEM; si->dma_rx_buff = dma_alloc_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, &si->dma_rx_buff_phy, GFP_KERNEL); @@ -737,9 +827,9 @@ err_irlap: err_dma_tx_buff: dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_rx_buff, si->dma_rx_buff_phy); err_dma_rx_buff: - pxa_free_dma(si->txdma); + dma_release_channel(si->txdma); err_tx_dma: - pxa_free_dma(si->rxdma); + dma_release_channel(si->rxdma); err_rx_dma: free_irq(si->icp_irq, dev); err_irq2: @@ -766,8 +856,10 @@ static int pxa_irda_stop(struct net_device *dev) free_irq(si->uart_irq, dev); free_irq(si->icp_irq, dev); - pxa_free_dma(si->rxdma); - pxa_free_dma(si->txdma); + dmaengine_terminate_all(si->rxdma); + dmaengine_terminate_all(si->txdma); + dma_release_channel(si->rxdma); + dma_release_channel(si->txdma); if (si->dma_rx_buff) dma_free_coherent(si->dev, IRDA_FRAME_SIZE_LIMIT, si->dma_tx_buff, si->dma_tx_buff_phy); @@ -830,25 +922,33 @@ static const struct net_device_ops pxa_irda_netdev_ops = { static int pxa_irda_probe(struct platform_device *pdev) { struct net_device *dev; + struct resource *res; struct pxa_irda *si; + void __iomem *ficp, *stuart; unsigned int baudrate_mask; int err; if (!pdev->dev.platform_data) return -ENODEV; - err = request_mem_region(__PREG(STUART), 0x24, "IrDA") ? 0 : -EBUSY; - if (err) - goto err_mem_1; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ficp = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ficp)) { + dev_err(&pdev->dev, "resource ficp not defined\n"); + return PTR_ERR(ficp); + } - err = request_mem_region(__PREG(FICP), 0x1c, "IrDA") ? 0 : -EBUSY; - if (err) - goto err_mem_2; + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + stuart = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(stuart)) { + dev_err(&pdev->dev, "resource stuart not defined\n"); + return PTR_ERR(stuart); + } dev = alloc_irdadev(sizeof(struct pxa_irda)); if (!dev) { err = -ENOMEM; - goto err_mem_3; + goto err_mem_1; } SET_NETDEV_DEV(dev, &pdev->dev); @@ -856,16 +956,25 @@ static int pxa_irda_probe(struct platform_device *pdev) si->dev = &pdev->dev; si->pdata = pdev->dev.platform_data; + si->irda_base = ficp; + si->stuart_base = stuart; si->uart_irq = platform_get_irq(pdev, 0); si->icp_irq = platform_get_irq(pdev, 1); - si->sir_clk = clk_get(&pdev->dev, "UARTCLK"); - si->fir_clk = clk_get(&pdev->dev, "FICPCLK"); + si->sir_clk = devm_clk_get(&pdev->dev, "UARTCLK"); + si->fir_clk = devm_clk_get(&pdev->dev, "FICPCLK"); if (IS_ERR(si->sir_clk) || IS_ERR(si->fir_clk)) { err = PTR_ERR(IS_ERR(si->sir_clk) ? si->sir_clk : si->fir_clk); goto err_mem_4; } + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res) + si->drcmr_rx = res->start; + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (res) + si->drcmr_tx = res->start; + /* * Initialise the SIR buffers */ @@ -925,15 +1034,7 @@ err_startup: err_mem_5: kfree(si->rx_buff.head); err_mem_4: - if (si->sir_clk && !IS_ERR(si->sir_clk)) - clk_put(si->sir_clk); - if (si->fir_clk && !IS_ERR(si->fir_clk)) - clk_put(si->fir_clk); free_netdev(dev); -err_mem_3: - release_mem_region(__PREG(FICP), 0x1c); -err_mem_2: - release_mem_region(__PREG(STUART), 0x24); } err_mem_1: return err; @@ -952,14 +1053,9 @@ static int pxa_irda_remove(struct platform_device *_dev) si->pdata->shutdown(si->dev); kfree(si->tx_buff.head); kfree(si->rx_buff.head); - clk_put(si->fir_clk); - clk_put(si->sir_clk); free_netdev(dev); } - release_mem_region(__PREG(STUART), 0x24); - release_mem_region(__PREG(FICP), 0x1c); - return 0; } diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 47da43595ac2..86f6c6292c27 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -412,7 +412,7 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) port = macvlan_port_get_rcu(skb->dev); if (is_multicast_ether_addr(eth->h_dest)) { - skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN); + skb = ip_check_defrag(dev_net(skb->dev), skb, IP_DEFRAG_MACVLAN); if (!skb) return RX_HANDLER_CONSUMED; eth = eth_hdr(skb); diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index edd77342773a..248478c6f6e4 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -1111,10 +1111,10 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, return 0; case TUNSETSNDBUF: - if (get_user(u, up)) + if (get_user(s, sp)) return -EFAULT; - q->sk.sk_sndbuf = u; + q->sk.sk_sndbuf = s; return 0; case TUNGETVNETHDRSZ: diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index d8757bf9ad75..a9acf7156855 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -61,11 +61,21 @@ MODULE_VERSION(NTB_NETDEV_VER); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Intel Corporation"); +/* Time in usecs for tx resource reaper */ +static unsigned int tx_time = 1; + +/* Number of descriptors to free before resuming tx */ +static unsigned int tx_start = 10; + +/* Number of descriptors still available before stop upper layer tx */ +static unsigned int tx_stop = 5; + struct ntb_netdev { struct list_head list; struct pci_dev *pdev; struct net_device *ndev; struct ntb_transport_qp *qp; + struct timer_list tx_timer; }; #define NTB_TX_TIMEOUT_MS 1000 @@ -136,11 +146,42 @@ enqueue_again: } } +static int __ntb_netdev_maybe_stop_tx(struct net_device *netdev, + struct ntb_transport_qp *qp, int size) +{ + struct ntb_netdev *dev = netdev_priv(netdev); + + netif_stop_queue(netdev); + /* Make sure to see the latest value of ntb_transport_tx_free_entry() + * since the queue was last started. + */ + smp_mb(); + + if (likely(ntb_transport_tx_free_entry(qp) < size)) { + mod_timer(&dev->tx_timer, jiffies + usecs_to_jiffies(tx_time)); + return -EBUSY; + } + + netif_start_queue(netdev); + return 0; +} + +static int ntb_netdev_maybe_stop_tx(struct net_device *ndev, + struct ntb_transport_qp *qp, int size) +{ + if (netif_queue_stopped(ndev) || + (ntb_transport_tx_free_entry(qp) >= size)) + return 0; + + return __ntb_netdev_maybe_stop_tx(ndev, qp, size); +} + static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data, void *data, int len) { struct net_device *ndev = qp_data; struct sk_buff *skb; + struct ntb_netdev *dev = netdev_priv(ndev); skb = data; if (!skb || !ndev) @@ -155,6 +196,15 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data, } dev_kfree_skb(skb); + + if (ntb_transport_tx_free_entry(dev->qp) >= tx_start) { + /* Make sure anybody stopping the queue after this sees the new + * value of ntb_transport_tx_free_entry() + */ + smp_mb(); + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + } } static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb, @@ -163,10 +213,15 @@ static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb, struct ntb_netdev *dev = netdev_priv(ndev); int rc; + ntb_netdev_maybe_stop_tx(ndev, dev->qp, tx_stop); + rc = ntb_transport_tx_enqueue(dev->qp, skb, skb->data, skb->len); if (rc) goto err; + /* check for next submit */ + ntb_netdev_maybe_stop_tx(ndev, dev->qp, tx_stop); + return NETDEV_TX_OK; err: @@ -175,6 +230,23 @@ err: return NETDEV_TX_BUSY; } +static void ntb_netdev_tx_timer(unsigned long data) +{ + struct net_device *ndev = (struct net_device *)data; + struct ntb_netdev *dev = netdev_priv(ndev); + + if (ntb_transport_tx_free_entry(dev->qp) < tx_stop) { + mod_timer(&dev->tx_timer, jiffies + msecs_to_jiffies(tx_time)); + } else { + /* Make sure anybody stopping the queue after this sees the new + * value of ntb_transport_tx_free_entry() + */ + smp_mb(); + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + } +} + static int ntb_netdev_open(struct net_device *ndev) { struct ntb_netdev *dev = netdev_priv(ndev); @@ -197,8 +269,11 @@ static int ntb_netdev_open(struct net_device *ndev) } } + setup_timer(&dev->tx_timer, ntb_netdev_tx_timer, (unsigned long)ndev); + netif_carrier_off(ndev); ntb_transport_link_up(dev->qp); + netif_start_queue(ndev); return 0; @@ -219,6 +294,8 @@ static int ntb_netdev_close(struct net_device *ndev) while ((skb = ntb_transport_rx_remove(dev->qp, &len))) dev_kfree_skb(skb); + del_timer_sync(&dev->tx_timer); + return 0; } diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index c5ad98ace5d0..9d097ae54fb2 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -69,20 +69,39 @@ config SMSC_PHY ---help--- Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs +config BCM_NET_PHYLIB + tristate + config BROADCOM_PHY tristate "Drivers for Broadcom PHYs" + select BCM_NET_PHYLIB ---help--- Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464, BCM5481 and BCM5482 PHYs. +config BCM_CYGNUS_PHY + tristate "Drivers for Broadcom Cygnus SoC internal PHY" + depends on ARCH_BCM_CYGNUS || COMPILE_TEST + depends on MDIO_BCM_IPROC + select BCM_NET_PHYLIB + ---help--- + This PHY driver is for the 1G internal PHYs of the Broadcom + Cygnus Family SoC. + + Currently supports internal PHY's used in the BCM11300, + BCM11320, BCM11350, BCM11360, BCM58300, BCM58302, + BCM58303 & BCM58305 Broadcom Cygnus SoCs. + config BCM63XX_PHY tristate "Drivers for Broadcom 63xx SOCs internal PHY" depends on BCM63XX + select BCM_NET_PHYLIB ---help--- Currently supports the 6348 and 6358 PHYs. config BCM7XXX_PHY tristate "Drivers for Broadcom 7xxx SOCs internal PHYs" + select BCM_NET_PHYLIB ---help--- Currently supports the BCM7366, BCM7439, BCM7445, and 40nm and 65nm generation of BCM7xxx Set Top Box SoCs. @@ -225,6 +244,15 @@ config MDIO_BCM_UNIMAC This hardware can be found in the Broadcom GENET Ethernet MAC controllers as well as some Broadcom Ethernet switches such as the Starfighter 2 switches. + +config MDIO_BCM_IPROC + tristate "Broadcom iProc MDIO bus controller" + depends on ARCH_BCM_IPROC || COMPILE_TEST + depends on HAS_IOMEM && OF_MDIO + help + This module provides a driver for the MDIO busses found in the + Broadcom iProc SoC's. + endif # PHYLIB config MICREL_KS8995MA diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 87f079c4b2c7..7655d47ad8d8 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -12,10 +12,12 @@ obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_TERANETICS_PHY) += teranetics.o obj-$(CONFIG_VITESSE_PHY) += vitesse.o +obj-$(CONFIG_BCM_NET_PHYLIB) += bcm-phy-lib.o obj-$(CONFIG_BROADCOM_PHY) += broadcom.o obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o obj-$(CONFIG_BCM7XXX_PHY) += bcm7xxx.o obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o +obj-$(CONFIG_BCM_CYGNUS_PHY) += bcm-cygnus.o obj-$(CONFIG_ICPLUS_PHY) += icplus.o obj-$(CONFIG_REALTEK_PHY) += realtek.o obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o @@ -38,3 +40,4 @@ obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o obj-$(CONFIG_MICROCHIP_PHY) += microchip.o +obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o diff --git a/drivers/net/phy/bcm-cygnus.c b/drivers/net/phy/bcm-cygnus.c new file mode 100644 index 000000000000..49bbc6826883 --- /dev/null +++ b/drivers/net/phy/bcm-cygnus.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* Broadcom Cygnus SoC internal transceivers support. */ +#include "bcm-phy-lib.h" +#include <linux/brcmphy.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/phy.h> + +/* Broadcom Cygnus Phy specific registers */ +#define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0 0x91E5 /* VDAL Control register */ + +static int bcm_cygnus_afe_config(struct phy_device *phydev) +{ + int rc; + + /* ensure smdspclk is enabled */ + rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 0x0c30); + if (rc < 0) + return rc; + + /* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */ + rc = bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8); + if (rc < 0) + return rc; + + /* AFE_HPF_TRIM_OTHERS bit11=1, short cascode enable for all modes*/ + rc = bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803); + if (rc < 0) + return rc; + + /* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */ + rc = bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740); + if (rc < 0) + return rc; + + /* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */ + rc = bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400); + if (rc < 0) + return rc; + + /* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */ + rc = bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004); + if (rc < 0) + return rc; + + /* Adjust bias current trim to overcome digital offSet */ + rc = phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x02); + if (rc < 0) + return rc; + + /* make rcal=100, since rdb default is 000 */ + rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB1, 0x10); + if (rc < 0) + return rc; + + /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */ + rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x10); + if (rc < 0) + return rc; + + /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */ + rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x00); + + return 0; +} + +static int bcm_cygnus_config_init(struct phy_device *phydev) +{ + int reg, rc; + + reg = phy_read(phydev, MII_BCM54XX_ECR); + if (reg < 0) + return reg; + + /* Mask interrupts globally. */ + reg |= MII_BCM54XX_ECR_IM; + rc = phy_write(phydev, MII_BCM54XX_ECR, reg); + if (rc) + return rc; + + /* Unmask events of interest */ + reg = ~(MII_BCM54XX_INT_DUPLEX | + MII_BCM54XX_INT_SPEED | + MII_BCM54XX_INT_LINK); + rc = phy_write(phydev, MII_BCM54XX_IMR, reg); + if (rc) + return rc; + + /* Apply AFE settings for the PHY */ + rc = bcm_cygnus_afe_config(phydev); + if (rc) + return rc; + + /* Advertise EEE */ + rc = bcm_phy_enable_eee(phydev); + if (rc) + return rc; + + /* Enable APD */ + return bcm_phy_enable_apd(phydev, false); +} + +static int bcm_cygnus_resume(struct phy_device *phydev) +{ + int rc; + + genphy_resume(phydev); + + /* Re-initialize the PHY to apply AFE work-arounds and + * configurations when coming out of suspend. + */ + rc = bcm_cygnus_config_init(phydev); + if (rc) + return rc; + + /* restart auto negotiation with the new settings */ + return genphy_config_aneg(phydev); +} + +static struct phy_driver bcm_cygnus_phy_driver[] = { +{ + .phy_id = PHY_ID_BCM_CYGNUS, + .phy_id_mask = 0xfffffff0, + .name = "Broadcom Cygnus PHY", + .features = PHY_GBIT_FEATURES | + SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .config_init = bcm_cygnus_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, + .suspend = genphy_suspend, + .resume = bcm_cygnus_resume, +} }; + +static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = { + { PHY_ID_BCM_CYGNUS, 0xfffffff0, }, + { } +}; +MODULE_DEVICE_TABLE(mdio, bcm_cygnus_phy_tbl); + +module_phy_driver(bcm_cygnus_phy_driver); + +MODULE_DESCRIPTION("Broadcom Cygnus internal PHY driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Broadcom Corporation"); diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c new file mode 100644 index 000000000000..dd79ea6ba023 --- /dev/null +++ b/drivers/net/phy/bcm-phy-lib.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "bcm-phy-lib.h" +#include <linux/brcmphy.h> +#include <linux/export.h> +#include <linux/mdio.h> +#include <linux/phy.h> + +#define MII_BCM_CHANNEL_WIDTH 0x2000 +#define BCM_CL45VEN_EEE_ADV 0x3c + +int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) +{ + int rc; + + rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); + if (rc < 0) + return rc; + + return phy_write(phydev, MII_BCM54XX_EXP_DATA, val); +} +EXPORT_SYMBOL_GPL(bcm_phy_write_exp); + +int bcm_phy_read_exp(struct phy_device *phydev, u16 reg) +{ + int val; + + val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); + if (val < 0) + return val; + + val = phy_read(phydev, MII_BCM54XX_EXP_DATA); + + /* Restore default value. It's O.K. if this write fails. */ + phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); + + return val; +} +EXPORT_SYMBOL_GPL(bcm_phy_read_exp); + +int bcm_phy_write_misc(struct phy_device *phydev, + u16 reg, u16 chl, u16 val) +{ + int rc; + int tmp; + + rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, + MII_BCM54XX_AUXCTL_SHDWSEL_MISC); + if (rc < 0) + return rc; + + tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); + tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; + rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); + if (rc < 0) + return rc; + + tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; + rc = bcm_phy_write_exp(phydev, tmp, val); + + return rc; +} +EXPORT_SYMBOL_GPL(bcm_phy_write_misc); + +int bcm_phy_read_misc(struct phy_device *phydev, + u16 reg, u16 chl) +{ + int rc; + int tmp; + + rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, + MII_BCM54XX_AUXCTL_SHDWSEL_MISC); + if (rc < 0) + return rc; + + tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); + tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; + rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); + if (rc < 0) + return rc; + + tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; + rc = bcm_phy_read_exp(phydev, tmp); + + return rc; +} +EXPORT_SYMBOL_GPL(bcm_phy_read_misc); + +int bcm_phy_ack_intr(struct phy_device *phydev) +{ + int reg; + + /* Clear pending interrupts. */ + reg = phy_read(phydev, MII_BCM54XX_ISR); + if (reg < 0) + return reg; + + return 0; +} +EXPORT_SYMBOL_GPL(bcm_phy_ack_intr); + +int bcm_phy_config_intr(struct phy_device *phydev) +{ + int reg; + + reg = phy_read(phydev, MII_BCM54XX_ECR); + if (reg < 0) + return reg; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + reg &= ~MII_BCM54XX_ECR_IM; + else + reg |= MII_BCM54XX_ECR_IM; + + return phy_write(phydev, MII_BCM54XX_ECR, reg); +} +EXPORT_SYMBOL_GPL(bcm_phy_config_intr); + +int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow) +{ + phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow)); + return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD)); +} +EXPORT_SYMBOL_GPL(bcm_phy_read_shadow); + +int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, + u16 val) +{ + return phy_write(phydev, MII_BCM54XX_SHD, + MII_BCM54XX_SHD_WRITE | + MII_BCM54XX_SHD_VAL(shadow) | + MII_BCM54XX_SHD_DATA(val)); +} +EXPORT_SYMBOL_GPL(bcm_phy_write_shadow); + +int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) +{ + int val; + + if (dll_pwr_down) { + val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); + if (val < 0) + return val; + + val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; + bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); + } + + val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); + if (val < 0) + return val; + + /* Clear APD bits */ + val &= BCM_APD_CLR_MASK; + + if (phydev->autoneg == AUTONEG_ENABLE) + val |= BCM54XX_SHD_APD_EN; + else + val |= BCM_NO_ANEG_APD_EN; + + /* Enable energy detect single link pulse for easy wakeup */ + val |= BCM_APD_SINGLELP_EN; + + /* Enable Auto Power-Down (APD) for the PHY */ + return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); +} +EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); + +int bcm_phy_enable_eee(struct phy_device *phydev) +{ + int val; + + /* Enable EEE at PHY level */ + val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, + MDIO_MMD_AN, phydev->addr); + if (val < 0) + return val; + + val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; + + phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, + MDIO_MMD_AN, phydev->addr, (u32)val); + + /* Advertise EEE */ + val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, + MDIO_MMD_AN, phydev->addr); + if (val < 0) + return val; + + val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); + + phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, + MDIO_MMD_AN, phydev->addr, (u32)val); + + return 0; +} +EXPORT_SYMBOL_GPL(bcm_phy_enable_eee); diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h new file mode 100644 index 000000000000..b2091c88b44d --- /dev/null +++ b/drivers/net/phy/bcm-phy-lib.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_BCM_PHY_LIB_H +#define _LINUX_BCM_PHY_LIB_H + +#include <linux/phy.h> + +int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val); +int bcm_phy_read_exp(struct phy_device *phydev, u16 reg); + +int bcm_phy_write_misc(struct phy_device *phydev, + u16 reg, u16 chl, u16 value); +int bcm_phy_read_misc(struct phy_device *phydev, + u16 reg, u16 chl); + +int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, + u16 val); +int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow); + +int bcm_phy_ack_intr(struct phy_device *phydev); +int bcm_phy_config_intr(struct phy_device *phydev); + +int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down); + +int bcm_phy_enable_eee(struct phy_device *phydev); +#endif /* _LINUX_BCM_PHY_LIB_H */ diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c index 830ec31f952f..86b28052bf06 100644 --- a/drivers/net/phy/bcm63xx.c +++ b/drivers/net/phy/bcm63xx.c @@ -6,6 +6,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#include "bcm-phy-lib.h" #include <linux/module.h> #include <linux/phy.h> @@ -42,35 +43,6 @@ static int bcm63xx_config_init(struct phy_device *phydev) return phy_write(phydev, MII_BCM63XX_IR, reg); } -static int bcm63xx_ack_interrupt(struct phy_device *phydev) -{ - int reg; - - /* Clear pending interrupts. */ - reg = phy_read(phydev, MII_BCM63XX_IR); - if (reg < 0) - return reg; - - return 0; -} - -static int bcm63xx_config_intr(struct phy_device *phydev) -{ - int reg, err; - - reg = phy_read(phydev, MII_BCM63XX_IR); - if (reg < 0) - return reg; - - if (phydev->interrupts == PHY_INTERRUPT_ENABLED) - reg &= ~MII_BCM63XX_IR_GMASK; - else - reg |= MII_BCM63XX_IR_GMASK; - - err = phy_write(phydev, MII_BCM63XX_IR, reg); - return err; -} - static struct phy_driver bcm63xx_driver[] = { { .phy_id = 0x00406000, @@ -82,8 +54,8 @@ static struct phy_driver bcm63xx_driver[] = { .config_init = bcm63xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm63xx_ack_interrupt, - .config_intr = bcm63xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { /* same phy as above, with just a different OUI */ @@ -95,8 +67,8 @@ static struct phy_driver bcm63xx_driver[] = { .config_init = bcm63xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm63xx_ack_interrupt, - .config_intr = bcm63xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, } }; diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 6b701b3ded74..03d4809a9126 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -12,12 +12,12 @@ #include <linux/module.h> #include <linux/phy.h> #include <linux/delay.h> +#include "bcm-phy-lib.h" #include <linux/bitops.h> #include <linux/brcmphy.h> #include <linux/mdio.h> /* Broadcom BCM7xxx internal PHY registers */ -#define MII_BCM7XXX_CHANNEL_WIDTH 0x2000 /* 40nm only register definitions */ #define MII_BCM7XXX_100TX_AUX_CTL 0x10 @@ -25,7 +25,6 @@ #define MII_BCM7XXX_100TX_DISC 0x14 #define MII_BCM7XXX_AUX_MODE 0x1d #define MII_BCM7XX_64CLK_MDIO BIT(12) -#define MII_BCM7XXX_CORE_BASE1E 0x1e #define MII_BCM7XXX_TEST 0x1f #define MII_BCM7XXX_SHD_MODE_2 BIT(2) @@ -46,39 +45,13 @@ #define AFE_VDAC_OTHERS_0 MISC_ADDR(0x39, 3) #define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0) -#define CORE_EXPB0 0xb0 - -static void phy_write_exp(struct phy_device *phydev, - u16 reg, u16 value) -{ - phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg); - phy_write(phydev, MII_BCM54XX_EXP_DATA, value); -} - -static void phy_write_misc(struct phy_device *phydev, - u16 reg, u16 chl, u16 value) -{ - int tmp; - - phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); - - tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); - tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; - phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); - - tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg; - phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp); - - phy_write(phydev, MII_BCM54XX_EXP_DATA, value); -} - static void r_rc_cal_reset(struct phy_device *phydev) { /* Reset R_CAL/RC_CAL Engine */ - phy_write_exp(phydev, 0x00b0, 0x0010); + bcm_phy_write_exp(phydev, 0x00b0, 0x0010); /* Disable Reset R_AL/RC_CAL Engine */ - phy_write_exp(phydev, 0x00b0, 0x0000); + bcm_phy_write_exp(phydev, 0x00b0, 0x0000); } static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) @@ -86,38 +59,38 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) /* Increase VCO range to prevent unlocking problem of PLL at low * temp */ - phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); + bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); /* Change Ki to 011 */ - phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); + bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); /* Disable loading of TVCO buffer to bandgap, set bandgap trim * to 111 */ - phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); + bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); /* Adjust bias current trim by -3 */ - phy_write_misc(phydev, DSP_TAP10, 0x690b); + bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); /* Switch to CORE_BASE1E */ - phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd); + phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); r_rc_cal_reset(phydev); /* write AFE_RXCONFIG_0 */ - phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); /* write AFE_RXCONFIG_1 */ - phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); /* write AFE_RX_LP_COUNTER */ - phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); + bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); /* write AFE_HPF_TRIM_OTHERS */ - phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); + bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); /* write AFTE_TX_CONFIG */ - phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); + bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); return 0; } @@ -125,36 +98,36 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) { /* AFE_RXCONFIG_0 */ - phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15); /* AFE_RXCONFIG_1 */ - phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); /* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */ - phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003); /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */ - phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); + bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ - phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); + bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ - phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); + bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); /* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */ - phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020); + bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020); /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal * offset for HT=0 code */ - phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); + bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ - phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); + phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010); /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ - phy_write_misc(phydev, DSP_TAP10, 0x011b); + bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); /* Reset R_CAL/RC_CAL engine */ r_rc_cal_reset(phydev); @@ -165,24 +138,24 @@ static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev) { /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */ - phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); + bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ - phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); + bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ - phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); + bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal * offset for HT=0 code */ - phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); + bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ - phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); + phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010); /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ - phy_write_misc(phydev, DSP_TAP10, 0x011b); + bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); /* Reset R_CAL/RC_CAL engine */ r_rc_cal_reset(phydev); @@ -190,53 +163,6 @@ static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev) return 0; } -static int bcm7xxx_apd_enable(struct phy_device *phydev) -{ - int val; - - /* Enable powering down of the DLL during auto-power down */ - val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3); - if (val < 0) - return val; - - val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; - bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val); - - /* Enable auto-power down */ - val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD); - if (val < 0) - return val; - - val |= BCM54XX_SHD_APD_EN; - return bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val); -} - -static int bcm7xxx_eee_enable(struct phy_device *phydev) -{ - int val; - - val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, - MDIO_MMD_AN, phydev->addr); - if (val < 0) - return val; - - /* Enable general EEE feature at the PHY level */ - val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; - - phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, - MDIO_MMD_AN, phydev->addr, val); - - /* Advertise supported modes */ - val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, - MDIO_MMD_AN, phydev->addr); - - val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); - phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, - MDIO_MMD_AN, phydev->addr, val); - - return 0; -} - static int bcm7xxx_28nm_config_init(struct phy_device *phydev) { u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags); @@ -273,11 +199,11 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev) if (ret) return ret; - ret = bcm7xxx_eee_enable(phydev); + ret = bcm_phy_enable_eee(phydev); if (ret) return ret; - return bcm7xxx_apd_enable(phydev); + return bcm_phy_enable_apd(phydev, true); } static int bcm7xxx_28nm_resume(struct phy_device *phydev) diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 9c71295f2fef..07a6119121c3 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -14,6 +14,7 @@ * 2 of the License, or (at your option) any later version. */ +#include "bcm-phy-lib.h" #include <linux/module.h> #include <linux/phy.h> #include <linux/brcmphy.h> @@ -29,39 +30,6 @@ MODULE_DESCRIPTION("Broadcom PHY driver"); MODULE_AUTHOR("Maciej W. Rozycki"); MODULE_LICENSE("GPL"); -/* Indirect register access functions for the Expansion Registers */ -static int bcm54xx_exp_read(struct phy_device *phydev, u16 regnum) -{ - int val; - - val = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum); - if (val < 0) - return val; - - val = phy_read(phydev, MII_BCM54XX_EXP_DATA); - - /* Restore default value. It's O.K. if this write fails. */ - phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); - - return val; -} - -static int bcm54xx_exp_write(struct phy_device *phydev, u16 regnum, u16 val) -{ - int ret; - - ret = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum); - if (ret < 0) - return ret; - - ret = phy_write(phydev, MII_BCM54XX_EXP_DATA, val); - - /* Restore default value. It's O.K. if this write fails. */ - phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); - - return ret; -} - static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) { return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); @@ -72,28 +40,28 @@ static int bcm50610_a0_workaround(struct phy_device *phydev) { int err; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH0, + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0, MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN | MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH3, - MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ); + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3, + MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, MII_BCM54XX_EXP_EXP75_VDACCTRL); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP96, + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96, MII_BCM54XX_EXP_EXP96_MYST); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP97, + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97, MII_BCM54XX_EXP_EXP97_MYST); return err; @@ -114,7 +82,7 @@ static int bcm54xx_phydsp_config(struct phy_device *phydev) if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) { /* Clear bit 9 to fix a phy interop issue. */ - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP08, + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, MII_BCM54XX_EXP_EXP08_RJCT_2MHZ); if (err < 0) goto error; @@ -129,12 +97,12 @@ static int bcm54xx_phydsp_config(struct phy_device *phydev) if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) { int val; - val = bcm54xx_exp_read(phydev, MII_BCM54XX_EXP_EXP75); + val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75); if (val < 0) goto error; val |= MII_BCM54XX_EXP_EXP75_CM_OSC; - err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, val); + err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val); } error: @@ -159,7 +127,7 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev) BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M) return; - val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3); + val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); if (val < 0) return; @@ -190,9 +158,9 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev) val |= BCM54XX_SHD_SCR3_TRDDAPD; if (orig != val) - bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val); + bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); - val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD); + val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); if (val < 0) return; @@ -204,7 +172,7 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev) val &= ~BCM54XX_SHD_APD_EN; if (orig != val) - bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val); + bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); } static int bcm54xx_config_init(struct phy_device *phydev) @@ -232,7 +200,7 @@ static int bcm54xx_config_init(struct phy_device *phydev) if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) && (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE)) - bcm54xx_shadow_write(phydev, BCM54XX_SHD_RGMII_MODE, 0); + bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0); if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) || (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) || @@ -254,8 +222,8 @@ static int bcm5482_config_init(struct phy_device *phydev) /* * Enable secondary SerDes and its use as an LED source */ - reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_SSD); - bcm54xx_shadow_write(phydev, BCM5482_SHD_SSD, + reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_SSD); + bcm_phy_write_shadow(phydev, BCM5482_SHD_SSD, reg | BCM5482_SHD_SSD_LEDM | BCM5482_SHD_SSD_EN); @@ -264,10 +232,10 @@ static int bcm5482_config_init(struct phy_device *phydev) * Enable SGMII slave mode and auto-detection */ reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD; - err = bcm54xx_exp_read(phydev, reg); + err = bcm_phy_read_exp(phydev, reg); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, reg, err | + err = bcm_phy_write_exp(phydev, reg, err | BCM5482_SSD_SGMII_SLAVE_EN | BCM5482_SSD_SGMII_SLAVE_AD); if (err < 0) @@ -277,10 +245,10 @@ static int bcm5482_config_init(struct phy_device *phydev) * Disable secondary SerDes powerdown */ reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD; - err = bcm54xx_exp_read(phydev, reg); + err = bcm_phy_read_exp(phydev, reg); if (err < 0) return err; - err = bcm54xx_exp_write(phydev, reg, + err = bcm_phy_write_exp(phydev, reg, err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN); if (err < 0) return err; @@ -288,15 +256,15 @@ static int bcm5482_config_init(struct phy_device *phydev) /* * Select 1000BASE-X register set (primary SerDes) */ - reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_MODE); - bcm54xx_shadow_write(phydev, BCM5482_SHD_MODE, + reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_MODE); + bcm_phy_write_shadow(phydev, BCM5482_SHD_MODE, reg | BCM5482_SHD_MODE_1000BX); /* * LED1=ACTIVITYLED, LED3=LINKSPD[2] * (Use LED1 as secondary SerDes ACTIVITY LED) */ - bcm54xx_shadow_write(phydev, BCM5482_SHD_LEDS1, + bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1, BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) | BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2)); @@ -334,35 +302,6 @@ static int bcm5482_read_status(struct phy_device *phydev) return err; } -static int bcm54xx_ack_interrupt(struct phy_device *phydev) -{ - int reg; - - /* Clear pending interrupts. */ - reg = phy_read(phydev, MII_BCM54XX_ISR); - if (reg < 0) - return reg; - - return 0; -} - -static int bcm54xx_config_intr(struct phy_device *phydev) -{ - int reg, err; - - reg = phy_read(phydev, MII_BCM54XX_ECR); - if (reg < 0) - return reg; - - if (phydev->interrupts == PHY_INTERRUPT_ENABLED) - reg &= ~MII_BCM54XX_ECR_IM; - else - reg |= MII_BCM54XX_ECR_IM; - - err = phy_write(phydev, MII_BCM54XX_ECR, reg); - return err; -} - static int bcm5481_config_aneg(struct phy_device *phydev) { int ret; @@ -519,8 +458,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5421, @@ -532,8 +471,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5461, @@ -545,8 +484,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM54616S, @@ -558,8 +497,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5464, @@ -571,8 +510,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5481, @@ -584,8 +523,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = bcm5481_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5482, @@ -597,8 +536,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm5482_config_init, .config_aneg = genphy_config_aneg, .read_status = bcm5482_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM50610, @@ -610,8 +549,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM50610M, @@ -623,8 +562,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM57780, @@ -636,8 +575,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = bcm54xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, - .ack_interrupt = bcm54xx_ack_interrupt, - .config_intr = bcm54xx_config_intr, + .ack_interrupt = bcm_phy_ack_intr, + .config_intr = bcm_phy_config_intr, .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCMAC131, diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index fb1299c6326e..e23bf5b90e17 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -220,7 +220,7 @@ int fixed_phy_update_state(struct phy_device *phydev, struct fixed_mdio_bus *fmb = &platform_fmb; struct fixed_phy *fp; - if (!phydev || !phydev->bus) + if (!phydev || phydev->bus != fmb->mii_bus) return -EINVAL; list_for_each_entry(fp, &fmb->phys, node) { diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index e6897b6a8a53..5de8d5827536 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -785,6 +785,7 @@ static int marvell_read_status(struct phy_device *phydev) int adv; int err; int lpa; + int lpagb; int status = 0; /* Update the link, but return if there @@ -802,10 +803,17 @@ static int marvell_read_status(struct phy_device *phydev) if (lpa < 0) return lpa; + lpagb = phy_read(phydev, MII_STAT1000); + if (lpagb < 0) + return lpagb; + adv = phy_read(phydev, MII_ADVERTISE); if (adv < 0) return adv; + phydev->lp_advertising = mii_stat1000_to_ethtool_lpa_t(lpagb) | + mii_lpa_to_ethtool_lpa_t(lpa); + lpa &= adv; if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) @@ -853,6 +861,7 @@ static int marvell_read_status(struct phy_device *phydev) phydev->speed = SPEED_10; phydev->pause = phydev->asym_pause = 0; + phydev->lp_advertising = 0; } return 0; diff --git a/drivers/net/phy/mdio-bcm-iproc.c b/drivers/net/phy/mdio-bcm-iproc.c new file mode 100644 index 000000000000..c0b4e65267af --- /dev/null +++ b/drivers/net/phy/mdio-bcm-iproc.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_mdio.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <linux/sched.h> + +#define IPROC_GPHY_MDCDIV 0x1a + +#define MII_CTRL_OFFSET 0x000 + +#define MII_CTRL_DIV_SHIFT 0 +#define MII_CTRL_PRE_SHIFT 7 +#define MII_CTRL_BUSY_SHIFT 8 + +#define MII_DATA_OFFSET 0x004 +#define MII_DATA_MASK 0xffff +#define MII_DATA_TA_SHIFT 16 +#define MII_DATA_TA_VAL 2 +#define MII_DATA_RA_SHIFT 18 +#define MII_DATA_PA_SHIFT 23 +#define MII_DATA_OP_SHIFT 28 +#define MII_DATA_OP_WRITE 1 +#define MII_DATA_OP_READ 2 +#define MII_DATA_SB_SHIFT 30 + +struct iproc_mdio_priv { + struct mii_bus *mii_bus; + void __iomem *base; +}; + +static inline int iproc_mdio_wait_for_idle(void __iomem *base) +{ + u32 val; + unsigned int timeout = 1000; /* loop for 1s */ + + do { + val = readl(base + MII_CTRL_OFFSET); + if ((val & BIT(MII_CTRL_BUSY_SHIFT)) == 0) + return 0; + + usleep_range(1000, 2000); + } while (timeout--); + + return -ETIMEDOUT; +} + +static inline void iproc_mdio_config_clk(void __iomem *base) +{ + u32 val; + + val = (IPROC_GPHY_MDCDIV << MII_CTRL_DIV_SHIFT) | + BIT(MII_CTRL_PRE_SHIFT); + writel(val, base + MII_CTRL_OFFSET); +} + +static int iproc_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct iproc_mdio_priv *priv = bus->priv; + u32 cmd; + int rc; + + rc = iproc_mdio_wait_for_idle(priv->base); + if (rc) + return rc; + + iproc_mdio_config_clk(priv->base); + + /* Prepare the read operation */ + cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | + (reg << MII_DATA_RA_SHIFT) | + (phy_id << MII_DATA_PA_SHIFT) | + BIT(MII_DATA_SB_SHIFT) | + (MII_DATA_OP_READ << MII_DATA_OP_SHIFT); + + writel(cmd, priv->base + MII_DATA_OFFSET); + + rc = iproc_mdio_wait_for_idle(priv->base); + if (rc) + return rc; + + cmd = readl(priv->base + MII_DATA_OFFSET) & MII_DATA_MASK; + + return cmd; +} + +static int iproc_mdio_write(struct mii_bus *bus, int phy_id, + int reg, u16 val) +{ + struct iproc_mdio_priv *priv = bus->priv; + u32 cmd; + int rc; + + rc = iproc_mdio_wait_for_idle(priv->base); + if (rc) + return rc; + + iproc_mdio_config_clk(priv->base); + + /* Prepare the write operation */ + cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | + (reg << MII_DATA_RA_SHIFT) | + (phy_id << MII_DATA_PA_SHIFT) | + BIT(MII_DATA_SB_SHIFT) | + (MII_DATA_OP_WRITE << MII_DATA_OP_SHIFT) | + ((u32)(val) & MII_DATA_MASK); + + writel(cmd, priv->base + MII_DATA_OFFSET); + + rc = iproc_mdio_wait_for_idle(priv->base); + if (rc) + return rc; + + return 0; +} + +static int iproc_mdio_probe(struct platform_device *pdev) +{ + struct iproc_mdio_priv *priv; + struct mii_bus *bus; + struct resource *res; + int rc; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + dev_err(&pdev->dev, "failed to ioremap register\n"); + return PTR_ERR(priv->base); + } + + priv->mii_bus = mdiobus_alloc(); + if (!priv->mii_bus) { + dev_err(&pdev->dev, "MDIO bus alloc failed\n"); + return -ENOMEM; + } + + bus = priv->mii_bus; + bus->priv = priv; + bus->name = "iProc MDIO bus"; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); + bus->parent = &pdev->dev; + bus->read = iproc_mdio_read; + bus->write = iproc_mdio_write; + + rc = of_mdiobus_register(bus, pdev->dev.of_node); + if (rc) { + dev_err(&pdev->dev, "MDIO bus registration failed\n"); + goto err_iproc_mdio; + } + + platform_set_drvdata(pdev, priv); + + dev_info(&pdev->dev, "Broadcom iProc MDIO bus at 0x%p\n", priv->base); + + return 0; + +err_iproc_mdio: + mdiobus_free(bus); + return rc; +} + +static int iproc_mdio_remove(struct platform_device *pdev) +{ + struct iproc_mdio_priv *priv = platform_get_drvdata(pdev); + + mdiobus_unregister(priv->mii_bus); + mdiobus_free(priv->mii_bus); + + return 0; +} + +static const struct of_device_id iproc_mdio_of_match[] = { + { .compatible = "brcm,iproc-mdio", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, iproc_mdio_of_match); + +static struct platform_driver iproc_mdio_driver = { + .driver = { + .name = "iproc-mdio", + .of_match_table = iproc_mdio_of_match, + }, + .probe = iproc_mdio_probe, + .remove = iproc_mdio_remove, +}; + +module_platform_driver(iproc_mdio_driver); + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:iproc-mdio"); diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index 6a52a7f0fa0d..4bde5e728fe0 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -244,6 +244,7 @@ static const struct of_device_id unimac_mdio_ids[] = { { .compatible = "brcm,unimac-mdio", }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, unimac_mdio_ids); static struct platform_driver unimac_mdio_driver = { .driver = { diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 7dc21e56a7aa..3bc9f03349f3 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -261,6 +261,7 @@ static const struct of_device_id mdio_gpio_of_match[] = { { .compatible = "virtual,mdio-gpio", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, mdio_gpio_of_match); static struct platform_driver mdio_gpio_driver = { .probe = mdio_gpio_probe, diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index 4d4d25efc1e1..280c7c311f72 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -113,18 +113,18 @@ int mdio_mux_init(struct device *dev, if (!parent_bus_node) return -ENODEV; - parent_bus = of_mdio_find_bus(parent_bus_node); - if (parent_bus == NULL) { - ret_val = -EPROBE_DEFER; - goto err_parent_bus; - } - pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL); if (pb == NULL) { ret_val = -ENOMEM; goto err_parent_bus; } + parent_bus = of_mdio_find_bus(parent_bus_node); + if (parent_bus == NULL) { + ret_val = -EPROBE_DEFER; + goto err_parent_bus; + } + pb->switch_data = data; pb->switch_fn = switch_fn; pb->current_child = -1; @@ -173,6 +173,10 @@ int mdio_mux_init(struct device *dev, dev_info(dev, "Version " DRV_VERSION "\n"); return 0; } + + /* balance the reference of_mdio_find_bus() took */ + put_device(&pb->mii_bus->dev); + err_parent_bus: of_node_put(parent_bus_node); return ret_val; @@ -189,6 +193,9 @@ void mdio_mux_uninit(void *mux_handle) mdiobus_free(cb->mii_bus); cb = cb->next; } + + /* balance the reference of_mdio_find_bus() in mdio_mux_init() took */ + put_device(&pb->mii_bus->dev); } EXPORT_SYMBOL_GPL(mdio_mux_uninit); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 02a4615b65f8..12f44c53cc8e 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -167,7 +167,9 @@ static int of_mdio_bus_match(struct device *dev, const void *mdio_bus_np) * of_mdio_find_bus - Given an mii_bus node, find the mii_bus. * @mdio_bus_np: Pointer to the mii_bus. * - * Returns a pointer to the mii_bus, or NULL if none found. + * Returns a reference to the mii_bus, or NULL if none found. The + * embedded struct device will have its reference count incremented, + * and this must be put once the bus is finished with. * * Because the association of a device_node and mii_bus is made via * of_mdiobus_register(), the mii_bus cannot be found before it is @@ -234,15 +236,18 @@ static inline void of_mdiobus_link_phydev(struct mii_bus *mdio, #endif /** - * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus + * __mdiobus_register - bring up all the PHYs on a given bus and attach them to bus * @bus: target mii_bus + * @owner: module containing bus accessor functions * * Description: Called by a bus driver to bring up all the PHYs - * on a given bus, and attach them to the bus. + * on a given bus, and attach them to the bus. Drivers should use + * mdiobus_register() rather than __mdiobus_register() unless they + * need to pass a specific owner module. * * Returns 0 on success or < 0 on error. */ -int mdiobus_register(struct mii_bus *bus) +int __mdiobus_register(struct mii_bus *bus, struct module *owner) { int i, err; @@ -253,6 +258,7 @@ int mdiobus_register(struct mii_bus *bus) BUG_ON(bus->state != MDIOBUS_ALLOCATED && bus->state != MDIOBUS_UNREGISTERED); + bus->owner = owner; bus->dev.parent = bus->parent; bus->dev.class = &mdio_bus_class; bus->dev.groups = NULL; @@ -288,13 +294,16 @@ int mdiobus_register(struct mii_bus *bus) error: while (--i >= 0) { - if (bus->phy_map[i]) - device_unregister(&bus->phy_map[i]->dev); + struct phy_device *phydev = bus->phy_map[i]; + if (phydev) { + phy_device_remove(phydev); + phy_device_free(phydev); + } } device_del(&bus->dev); return err; } -EXPORT_SYMBOL(mdiobus_register); +EXPORT_SYMBOL(__mdiobus_register); void mdiobus_unregister(struct mii_bus *bus) { @@ -304,9 +313,11 @@ void mdiobus_unregister(struct mii_bus *bus) bus->state = MDIOBUS_UNREGISTERED; for (i = 0; i < PHY_MAX_ADDR; i++) { - if (bus->phy_map[i]) - device_unregister(&bus->phy_map[i]->dev); - bus->phy_map[i] = NULL; + struct phy_device *phydev = bus->phy_map[i]; + if (phydev) { + phy_device_remove(phydev); + phy_device_free(phydev); + } } device_del(&bus->dev); } diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index c0f211127274..383389146099 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -384,6 +384,24 @@ int phy_device_register(struct phy_device *phydev) EXPORT_SYMBOL(phy_device_register); /** + * phy_device_remove - Remove a previously registered phy device from the MDIO bus + * @phydev: phy_device structure to remove + * + * This doesn't free the phy_device itself, it merely reverses the effects + * of phy_device_register(). Use phy_device_free() to free the device + * after calling this function. + */ +void phy_device_remove(struct phy_device *phydev) +{ + struct mii_bus *bus = phydev->bus; + int addr = phydev->addr; + + device_del(&phydev->dev); + bus->phy_map[addr] = NULL; +} +EXPORT_SYMBOL(phy_device_remove); + +/** * phy_find_first - finds the first PHY device on the bus * @bus: the target MII bus */ @@ -578,14 +596,22 @@ EXPORT_SYMBOL(phy_init_hw); * generic driver is used. The phy_device is given a ptr to * the attaching device, and given a callback for link status * change. The phy_device is returned to the attaching driver. + * This function takes a reference on the phy device. */ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, u32 flags, phy_interface_t interface) { + struct mii_bus *bus = phydev->bus; struct device *d = &phydev->dev; - struct module *bus_module; int err; + if (!try_module_get(bus->owner)) { + dev_err(&dev->dev, "failed to get the bus module\n"); + return -EIO; + } + + get_device(d); + /* Assume that if there is no driver, that it doesn't * exist, and we should use the genphy driver. */ @@ -600,20 +626,13 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, err = device_bind_driver(d); if (err) - return err; + goto error; } if (phydev->attached_dev) { dev_err(&dev->dev, "PHY already attached\n"); - return -EBUSY; - } - - /* Increment the bus module reference count */ - bus_module = phydev->bus->dev.driver ? - phydev->bus->dev.driver->owner : NULL; - if (!try_module_get(bus_module)) { - dev_err(&dev->dev, "failed to get the bus module\n"); - return -EIO; + err = -EBUSY; + goto error; } phydev->attached_dev = dev; @@ -636,6 +655,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, phy_resume(phydev); return err; + +error: + put_device(d); + module_put(bus->owner); + return err; } EXPORT_SYMBOL(phy_attach_direct); @@ -677,14 +701,15 @@ EXPORT_SYMBOL(phy_attach); /** * phy_detach - detach a PHY device from its network device * @phydev: target phy_device struct + * + * This detaches the phy device from its network device and the phy + * driver, and drops the reference count taken in phy_attach_direct(). */ void phy_detach(struct phy_device *phydev) { + struct mii_bus *bus; int i; - if (phydev->bus->dev.driver) - module_put(phydev->bus->dev.driver->owner); - phydev->attached_dev->phydev = NULL; phydev->attached_dev = NULL; phy_suspend(phydev); @@ -700,6 +725,15 @@ void phy_detach(struct phy_device *phydev) break; } } + + /* + * The phydev might go away on the put_device() below, so avoid + * a use-after-free bug by reading the underlying bus first. + */ + bus = phydev->bus; + + put_device(&phydev->dev); + module_put(bus->owner); } EXPORT_SYMBOL(phy_detach); @@ -1205,6 +1239,44 @@ static int gen10g_resume(struct phy_device *phydev) return 0; } +static int __set_phy_supported(struct phy_device *phydev, u32 max_speed) +{ + /* The default values for phydev->supported are provided by the PHY + * driver "features" member, we want to reset to sane defaults first + * before supporting higher speeds. + */ + phydev->supported &= PHY_DEFAULT_FEATURES; + + switch (max_speed) { + default: + return -ENOTSUPP; + case SPEED_1000: + phydev->supported |= PHY_1000BT_FEATURES; + /* fall through */ + case SPEED_100: + phydev->supported |= PHY_100BT_FEATURES; + /* fall through */ + case SPEED_10: + phydev->supported |= PHY_10BT_FEATURES; + } + + return 0; +} + +int phy_set_max_speed(struct phy_device *phydev, u32 max_speed) +{ + int err; + + err = __set_phy_supported(phydev, max_speed); + if (err) + return err; + + phydev->advertising = phydev->supported; + + return 0; +} +EXPORT_SYMBOL(phy_set_max_speed); + static void of_set_phy_supported(struct phy_device *phydev) { struct device_node *node = phydev->dev.of_node; @@ -1216,25 +1288,8 @@ static void of_set_phy_supported(struct phy_device *phydev) if (!node) return; - if (!of_property_read_u32(node, "max-speed", &max_speed)) { - /* The default values for phydev->supported are provided by the PHY - * driver "features" member, we want to reset to sane defaults fist - * before supporting higher speeds. - */ - phydev->supported &= PHY_DEFAULT_FEATURES; - - switch (max_speed) { - default: - return; - - case SPEED_1000: - phydev->supported |= PHY_1000BT_FEATURES; - case SPEED_100: - phydev->supported |= PHY_100BT_FEATURES; - case SPEED_10: - phydev->supported |= PHY_10BT_FEATURES; - } - } + if (!of_property_read_u32(node, "max-speed", &max_speed)) + __set_phy_supported(phydev, max_speed); } /** diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c index 17cad185169d..76cad712ddb2 100644 --- a/drivers/net/phy/vitesse.c +++ b/drivers/net/phy/vitesse.c @@ -66,7 +66,6 @@ #define PHY_ID_VSC8244 0x000fc6c0 #define PHY_ID_VSC8514 0x00070670 #define PHY_ID_VSC8574 0x000704a0 -#define PHY_ID_VSC8641 0x00070431 #define PHY_ID_VSC8662 0x00070660 #define PHY_ID_VSC8221 0x000fc550 #define PHY_ID_VSC8211 0x000fc4b0 @@ -273,18 +272,6 @@ static struct phy_driver vsc82xx_driver[] = { .config_intr = &vsc82xx_config_intr, .driver = { .owner = THIS_MODULE,}, }, { - .phy_id = PHY_ID_VSC8641, - .name = "Vitesse VSC8641", - .phy_id_mask = 0x000ffff0, - .features = PHY_GBIT_FEATURES, - .flags = PHY_HAS_INTERRUPT, - .config_init = &vsc824x_config_init, - .config_aneg = &vsc82x4_config_aneg, - .read_status = &genphy_read_status, - .ack_interrupt = &vsc824x_ack_interrupt, - .config_intr = &vsc82xx_config_intr, - .driver = { .owner = THIS_MODULE,}, -}, { .phy_id = PHY_ID_VSC8662, .name = "Vitesse VSC8662", .phy_id_mask = 0x000ffff0, @@ -331,7 +318,6 @@ static struct mdio_device_id __maybe_unused vitesse_tbl[] = { { PHY_ID_VSC8244, 0x000fffc0 }, { PHY_ID_VSC8514, 0x000ffff0 }, { PHY_ID_VSC8574, 0x000ffff0 }, - { PHY_ID_VSC8641, 0x000ffff0 }, { PHY_ID_VSC8662, 0x000ffff0 }, { PHY_ID_VSC8221, 0x000ffff0 }, { PHY_ID_VSC8211, 0x000ffff0 }, diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 0481daf9201a..ed00446759b2 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -2755,6 +2755,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit, */ dev_net_set(dev, net); + rtnl_lock(); mutex_lock(&pn->all_ppp_mutex); if (unit < 0) { @@ -2785,7 +2786,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit, ppp->file.index = unit; sprintf(dev->name, "ppp%d", unit); - ret = register_netdev(dev); + ret = register_netdevice(dev); if (ret != 0) { unit_put(&pn->units_idr, unit); netdev_err(ppp->dev, "PPP: couldn't register device %s (%d)\n", @@ -2797,6 +2798,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit, atomic_inc(&ppp_unit_count); mutex_unlock(&pn->all_ppp_mutex); + rtnl_unlock(); *retp = 0; return ppp; diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 686f37daa262..fc69e41d0950 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -169,6 +169,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) { struct sock *sk = (struct sock *) chan->private; struct pppox_sock *po = pppox_sk(sk); + struct net *net = sock_net(sk); struct pptp_opt *opt = &po->proto.pptp; struct pptp_gre_header *hdr; unsigned int header_len = sizeof(*hdr); @@ -187,7 +188,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) if (sk_pppox(po)->sk_state & PPPOX_DEAD) goto tx_error; - rt = ip_route_output_ports(sock_net(sk), &fl4, NULL, + rt = ip_route_output_ports(net, &fl4, NULL, opt->dst_addr.sin_addr.s_addr, opt->src_addr.sin_addr.s_addr, 0, 0, IPPROTO_GRE, @@ -279,10 +280,10 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) nf_reset(skb); skb->ip_summed = CHECKSUM_NONE; - ip_select_ident(sock_net(sk), skb, NULL); + ip_select_ident(net, skb, NULL); ip_send_check(iph); - ip_local_out(skb); + ip_local_out(net, skb->sk, skb); return 1; tx_error: diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 976aa9704297..b1878faea397 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -858,7 +858,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) goto drop; - if (skb->sk) { + if (skb->sk && sk_fullsock(skb->sk)) { sock_tx_timestamp(skb->sk, &skb_shinfo(skb)->tx_flags); sw_tx_timestamp(skb); } diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 167cfc503a78..3a8a36c8ded1 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -585,4 +585,15 @@ config USB_VL600 http://ubuntuforums.org/showpost.php?p=10589647&postcount=17 +config USB_NET_CH9200 + tristate "QingHeng CH9200 USB ethernet support" + depends on USB_USBNET + select MII + help + Choose this option if you have a USB ethernet adapter with a QinHeng + CH9200 chipset. + + To compile this driver as a module, choose M here: the + module will be called ch9200. + endif # USB_NET_DRIVERS diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index cf6a0e610a7f..b5f04068dbe4 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -38,4 +38,4 @@ obj-$(CONFIG_USB_NET_HUAWEI_CDC_NCM) += huawei_cdc_ncm.o obj-$(CONFIG_USB_VL600) += lg-vl600.o obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o obj-$(CONFIG_USB_NET_CDC_MBIM) += cdc_mbim.o - +obj-$(CONFIG_USB_NET_CH9200) += ch9200.o diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h index 5d049d00c2d7..a2d3ea6efb20 100644 --- a/drivers/net/usb/asix.h +++ b/drivers/net/usb/asix.h @@ -168,7 +168,7 @@ struct asix_data { struct asix_rx_fixup_info { struct sk_buff *ax_skb; u32 header; - u16 size; + u16 remaining; bool split_head; }; diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 75d6f26729a3..a186b0a12d50 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -54,69 +54,101 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, struct asix_rx_fixup_info *rx) { int offset = 0; + u16 size; + + /* When an Ethernet frame spans multiple URB socket buffers, + * do a sanity test for the Data header synchronisation. + * Attempt to detect the situation of the previous socket buffer having + * been truncated or a socket buffer was missing. These situations + * cause a discontinuity in the data stream and therefore need to avoid + * appending bad data to the end of the current netdev socket buffer. + * Also avoid unnecessarily discarding a good current netdev socket + * buffer. + */ + if (rx->remaining && (rx->remaining + sizeof(u32) <= skb->len)) { + offset = ((rx->remaining + 1) & 0xfffe) + sizeof(u32); + rx->header = get_unaligned_le32(skb->data + offset); + offset = 0; + + size = (u16)(rx->header & 0x7ff); + if (size != ((~rx->header >> 16) & 0x7ff)) { + netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n", + rx->remaining); + if (rx->ax_skb) { + kfree_skb(rx->ax_skb); + rx->ax_skb = NULL; + /* Discard the incomplete netdev Ethernet frame + * and assume the Data header is at the start of + * the current URB socket buffer. + */ + } + rx->remaining = 0; + } + } while (offset + sizeof(u16) <= skb->len) { - u16 remaining = 0; + u16 copy_length; unsigned char *data; - if (!rx->size) { - if ((skb->len - offset == sizeof(u16)) || - rx->split_head) { - if(!rx->split_head) { - rx->header = get_unaligned_le16( - skb->data + offset); - rx->split_head = true; - offset += sizeof(u16); - break; - } else { - rx->header |= (get_unaligned_le16( - skb->data + offset) - << 16); - rx->split_head = false; - offset += sizeof(u16); - } + if (!rx->remaining) { + if (skb->len - offset == sizeof(u16)) { + rx->header = get_unaligned_le16( + skb->data + offset); + rx->split_head = true; + offset += sizeof(u16); + break; + } + + if (rx->split_head == true) { + rx->header |= (get_unaligned_le16( + skb->data + offset) << 16); + rx->split_head = false; + offset += sizeof(u16); } else { rx->header = get_unaligned_le32(skb->data + offset); offset += sizeof(u32); } - /* get the packet length */ - rx->size = (u16) (rx->header & 0x7ff); - if (rx->size != ((~rx->header >> 16) & 0x7ff)) { + /* take frame length from Data header 32-bit word */ + size = (u16)(rx->header & 0x7ff); + if (size != ((~rx->header >> 16) & 0x7ff)) { netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n", rx->header, offset); - rx->size = 0; return 0; } - rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, - rx->size); - if (!rx->ax_skb) + if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { + netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", + size); return 0; - } + } - if (rx->size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { - netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", - rx->size); - kfree_skb(rx->ax_skb); - rx->ax_skb = NULL; - rx->size = 0U; + /* Sometimes may fail to get a netdev socket buffer but + * continue to process the URB socket buffer so that + * synchronisation of the Ethernet frame Data header + * word is maintained. + */ + rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, size); - return 0; + rx->remaining = size; } - if (rx->size > skb->len - offset) { - remaining = rx->size - (skb->len - offset); - rx->size = skb->len - offset; + if (rx->remaining > skb->len - offset) { + copy_length = skb->len - offset; + rx->remaining -= copy_length; + } else { + copy_length = rx->remaining; + rx->remaining = 0; } - data = skb_put(rx->ax_skb, rx->size); - memcpy(data, skb->data + offset, rx->size); - if (!remaining) - usbnet_skb_return(dev, rx->ax_skb); + if (rx->ax_skb) { + data = skb_put(rx->ax_skb, copy_length); + memcpy(data, skb->data + offset, copy_length); + if (!rx->remaining) + usbnet_skb_return(dev, rx->ax_skb); + } - offset += (rx->size + 1) & 0xfffe; - rx->size = remaining; + offset += (copy_length + 1) & 0xfffe; } if (skb->len != offset) { diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index efc18e05af0a..bbde9884ab8a 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -342,7 +342,7 @@ static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci) in6_dev_put(in6_dev); /* ipv6_stub != NULL if in6_dev_get returned an inet6_dev */ - ipv6_stub->ndisc_send_na(netdev, NULL, &iph->saddr, &msg->target, + ipv6_stub->ndisc_send_na(netdev, &iph->saddr, &msg->target, is_router /* router */, true /* solicited */, false /* override */, diff --git a/drivers/net/usb/ch9200.c b/drivers/net/usb/ch9200.c new file mode 100644 index 000000000000..5e151e6a3e09 --- /dev/null +++ b/drivers/net/usb/ch9200.c @@ -0,0 +1,432 @@ +/* + * USB 10M/100M ethernet adapter + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/crc32.h> +#include <linux/usb/usbnet.h> +#include <linux/slab.h> + +#define CH9200_VID 0x1A86 +#define CH9200_PID_E092 0xE092 + +#define CTRL_TIMEOUT_MS 1000 + +#define CONTROL_TIMEOUT_MS 1000 + +#define REQUEST_READ 0x0E +#define REQUEST_WRITE 0x0F + +/* Address space: + * 00-63 : MII + * 64-128: MAC + * + * Note: all accesses must be 16-bit + */ + +#define MAC_REG_CTRL 64 +#define MAC_REG_STATUS 66 +#define MAC_REG_INTERRUPT_MASK 68 +#define MAC_REG_PHY_COMMAND 70 +#define MAC_REG_PHY_DATA 72 +#define MAC_REG_STATION_L 74 +#define MAC_REG_STATION_M 76 +#define MAC_REG_STATION_H 78 +#define MAC_REG_HASH_L 80 +#define MAC_REG_HASH_M1 82 +#define MAC_REG_HASH_M2 84 +#define MAC_REG_HASH_H 86 +#define MAC_REG_THRESHOLD 88 +#define MAC_REG_FIFO_DEPTH 90 +#define MAC_REG_PAUSE 92 +#define MAC_REG_FLOW_CONTROL 94 + +/* Control register bits + * + * Note: bits 13 and 15 are reserved + */ +#define LOOPBACK (0x01 << 14) +#define BASE100X (0x01 << 12) +#define MBPS_10 (0x01 << 11) +#define DUPLEX_MODE (0x01 << 10) +#define PAUSE_FRAME (0x01 << 9) +#define PROMISCUOUS (0x01 << 8) +#define MULTICAST (0x01 << 7) +#define BROADCAST (0x01 << 6) +#define HASH (0x01 << 5) +#define APPEND_PAD (0x01 << 4) +#define APPEND_CRC (0x01 << 3) +#define TRANSMITTER_ACTION (0x01 << 2) +#define RECEIVER_ACTION (0x01 << 1) +#define DMA_ACTION (0x01 << 0) + +/* Status register bits + * + * Note: bits 7-15 are reserved + */ +#define ALIGNMENT (0x01 << 6) +#define FIFO_OVER_RUN (0x01 << 5) +#define FIFO_UNDER_RUN (0x01 << 4) +#define RX_ERROR (0x01 << 3) +#define RX_COMPLETE (0x01 << 2) +#define TX_ERROR (0x01 << 1) +#define TX_COMPLETE (0x01 << 0) + +/* FIFO depth register bits + * + * Note: bits 6 and 14 are reserved + */ + +#define ETH_TXBD (0x01 << 15) +#define ETN_TX_FIFO_DEPTH (0x01 << 8) +#define ETH_RXBD (0x01 << 7) +#define ETH_RX_FIFO_DEPTH (0x01 << 0) + +static int control_read(struct usbnet *dev, + unsigned char request, unsigned short value, + unsigned short index, void *data, unsigned short size, + int timeout) +{ + unsigned char *buf = NULL; + unsigned char request_type; + int err = 0; + + if (request == REQUEST_READ) + request_type = (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER); + else + request_type = (USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE); + + netdev_dbg(dev->net, "Control_read() index=0x%02x size=%d\n", + index, size); + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto err_out; + } + + err = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + request, request_type, value, index, buf, size, + timeout); + if (err == size) + memcpy(data, buf, size); + else if (err >= 0) + err = -EINVAL; + kfree(buf); + + return err; + +err_out: + return err; +} + +static int control_write(struct usbnet *dev, unsigned char request, + unsigned short value, unsigned short index, + void *data, unsigned short size, int timeout) +{ + unsigned char *buf = NULL; + unsigned char request_type; + int err = 0; + + if (request == REQUEST_WRITE) + request_type = (USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_OTHER); + else + request_type = (USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE); + + netdev_dbg(dev->net, "Control_write() index=0x%02x size=%d\n", + index, size); + + if (data) { + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto err_out; + } + memcpy(buf, data, size); + } + + err = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + request, request_type, value, index, buf, size, + timeout); + if (err >= 0 && err < size) + err = -EINVAL; + kfree(buf); + + return 0; + +err_out: + return err; +} + +static int ch9200_mdio_read(struct net_device *netdev, int phy_id, int loc) +{ + struct usbnet *dev = netdev_priv(netdev); + unsigned char buff[2]; + + netdev_dbg(netdev, "ch9200_mdio_read phy_id:%02x loc:%02x\n", + phy_id, loc); + + if (phy_id != 0) + return -ENODEV; + + control_read(dev, REQUEST_READ, 0, loc * 2, buff, 0x02, + CONTROL_TIMEOUT_MS); + + return (buff[0] | buff[1] << 8); +} + +static void ch9200_mdio_write(struct net_device *netdev, + int phy_id, int loc, int val) +{ + struct usbnet *dev = netdev_priv(netdev); + unsigned char buff[2]; + + netdev_dbg(netdev, "ch9200_mdio_write() phy_id=%02x loc:%02x\n", + phy_id, loc); + + if (phy_id != 0) + return; + + buff[0] = (unsigned char)val; + buff[1] = (unsigned char)(val >> 8); + + control_write(dev, REQUEST_WRITE, 0, loc * 2, buff, 0x02, + CONTROL_TIMEOUT_MS); +} + +static int ch9200_link_reset(struct usbnet *dev) +{ + struct ethtool_cmd ecmd; + + mii_check_media(&dev->mii, 1, 1); + mii_ethtool_gset(&dev->mii, &ecmd); + + netdev_dbg(dev->net, "link_reset() speed:%d duplex:%d\n", + ecmd.speed, ecmd.duplex); + + return 0; +} + +static void ch9200_status(struct usbnet *dev, struct urb *urb) +{ + int link; + unsigned char *buf; + + if (urb->actual_length < 16) + return; + + buf = urb->transfer_buffer; + link = !!(buf[0] & 0x01); + + if (link) { + netif_carrier_on(dev->net); + usbnet_defer_kevent(dev, EVENT_LINK_RESET); + } else { + netif_carrier_off(dev->net); + } +} + +static struct sk_buff *ch9200_tx_fixup(struct usbnet *dev, struct sk_buff *skb, + gfp_t flags) +{ + int i = 0; + int len = 0; + int tx_overhead = 0; + + tx_overhead = 0x40; + + len = skb->len; + if (skb_headroom(skb) < tx_overhead) { + struct sk_buff *skb2; + + skb2 = skb_copy_expand(skb, tx_overhead, 0, flags); + dev_kfree_skb_any(skb); + skb = skb2; + if (!skb) + return NULL; + } + + __skb_push(skb, tx_overhead); + /* usbnet adds padding if length is a multiple of packet size + * if so, adjust length value in header + */ + if ((skb->len % dev->maxpacket) == 0) + len++; + + skb->data[0] = len; + skb->data[1] = len >> 8; + skb->data[2] = 0x00; + skb->data[3] = 0x80; + + for (i = 4; i < 48; i++) + skb->data[i] = 0x00; + + skb->data[48] = len; + skb->data[49] = len >> 8; + skb->data[50] = 0x00; + skb->data[51] = 0x80; + + for (i = 52; i < 64; i++) + skb->data[i] = 0x00; + + return skb; +} + +static int ch9200_rx_fixup(struct usbnet *dev, struct sk_buff *skb) +{ + int len = 0; + int rx_overhead = 0; + + rx_overhead = 64; + + if (unlikely(skb->len < rx_overhead)) { + dev_err(&dev->udev->dev, "unexpected tiny rx frame\n"); + return 0; + } + + len = (skb->data[skb->len - 16] | skb->data[skb->len - 15] << 8); + skb_trim(skb, len); + + return 1; +} + +static int get_mac_address(struct usbnet *dev, unsigned char *data) +{ + int err = 0; + unsigned char mac_addr[0x06]; + int rd_mac_len = 0; + + netdev_dbg(dev->net, "get_mac_address:\n\tusbnet VID:%0x PID:%0x\n", + dev->udev->descriptor.idVendor, + dev->udev->descriptor.idProduct); + + memset(mac_addr, 0, sizeof(mac_addr)); + rd_mac_len = control_read(dev, REQUEST_READ, 0, + MAC_REG_STATION_L, mac_addr, 0x02, + CONTROL_TIMEOUT_MS); + rd_mac_len += control_read(dev, REQUEST_READ, 0, MAC_REG_STATION_M, + mac_addr + 2, 0x02, CONTROL_TIMEOUT_MS); + rd_mac_len += control_read(dev, REQUEST_READ, 0, MAC_REG_STATION_H, + mac_addr + 4, 0x02, CONTROL_TIMEOUT_MS); + if (rd_mac_len != ETH_ALEN) + err = -EINVAL; + + data[0] = mac_addr[5]; + data[1] = mac_addr[4]; + data[2] = mac_addr[3]; + data[3] = mac_addr[2]; + data[4] = mac_addr[1]; + data[5] = mac_addr[0]; + + return err; +} + +static int ch9200_bind(struct usbnet *dev, struct usb_interface *intf) +{ + int retval = 0; + unsigned char data[2]; + + retval = usbnet_get_endpoints(dev, intf); + if (retval) + return retval; + + dev->mii.dev = dev->net; + dev->mii.mdio_read = ch9200_mdio_read; + dev->mii.mdio_write = ch9200_mdio_write; + dev->mii.reg_num_mask = 0x1f; + + dev->mii.phy_id_mask = 0x1f; + + dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; + dev->rx_urb_size = 24 * 64 + 16; + mii_nway_restart(&dev->mii); + + data[0] = 0x01; + data[1] = 0x0F; + retval = control_write(dev, REQUEST_WRITE, 0, MAC_REG_THRESHOLD, data, + 0x02, CONTROL_TIMEOUT_MS); + + data[0] = 0xA0; + data[1] = 0x90; + retval = control_write(dev, REQUEST_WRITE, 0, MAC_REG_FIFO_DEPTH, data, + 0x02, CONTROL_TIMEOUT_MS); + + data[0] = 0x30; + data[1] = 0x00; + retval = control_write(dev, REQUEST_WRITE, 0, MAC_REG_PAUSE, data, + 0x02, CONTROL_TIMEOUT_MS); + + data[0] = 0x17; + data[1] = 0xD8; + retval = control_write(dev, REQUEST_WRITE, 0, MAC_REG_FLOW_CONTROL, + data, 0x02, CONTROL_TIMEOUT_MS); + + /* Undocumented register */ + data[0] = 0x01; + data[1] = 0x00; + retval = control_write(dev, REQUEST_WRITE, 0, 254, data, 0x02, + CONTROL_TIMEOUT_MS); + + data[0] = 0x5F; + data[1] = 0x0D; + retval = control_write(dev, REQUEST_WRITE, 0, MAC_REG_CTRL, data, 0x02, + CONTROL_TIMEOUT_MS); + + retval = get_mac_address(dev, dev->net->dev_addr); + + return retval; +} + +static const struct driver_info ch9200_info = { + .description = "CH9200 USB to Network Adaptor", + .flags = FLAG_ETHER, + .bind = ch9200_bind, + .rx_fixup = ch9200_rx_fixup, + .tx_fixup = ch9200_tx_fixup, + .status = ch9200_status, + .link_reset = ch9200_link_reset, + .reset = ch9200_link_reset, +}; + +static const struct usb_device_id ch9200_products[] = { + { + USB_DEVICE(0x1A86, 0xE092), + .driver_info = (unsigned long)&ch9200_info, + }, + {}, +}; + +MODULE_DEVICE_TABLE(usb, ch9200_products); + +static struct usb_driver ch9200_driver = { + .name = "ch9200", + .id_table = ch9200_products, + .probe = usbnet_probe, + .disconnect = usbnet_disconnect, + .suspend = usbnet_suspend, + .resume = usbnet_resume, +}; + +module_usb_driver(ch9200_driver); + +MODULE_DESCRIPTION("QinHeng CH9200 USB Network device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 6a70a72b565d..226668ead0d8 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -2424,11 +2424,6 @@ static int lan78xx_rx(struct lan78xx_net *dev, struct sk_buff *skb) skb_pull(skb, align_count); } - if (unlikely(skb->len < 0)) { - netdev_warn(dev->net, "invalid rx length<0 %d", skb->len); - return 0; - } - return 1; } @@ -3209,7 +3204,6 @@ int lan78xx_suspend(struct usb_interface *intf, pm_message_t message) int ret; int event; - ret = 0; event = message.event; if (!dev->suspend_count++) { @@ -3291,6 +3285,7 @@ int lan78xx_suspend(struct usb_interface *intf, pm_message_t message) } } + ret = 0; out: return ret; } diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index d9e7892262fa..30033dbe6662 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -2185,11 +2185,6 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) skb_pull(skb, align_count); } - if (unlikely(skb->len < 0)) { - netdev_warn(dev->net, "invalid rx length<0 %d\n", skb->len); - return 0; - } - return 1; } diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 26423adc35ee..66b3ab9f614e 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -1815,11 +1815,6 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) skb_pull(skb, align_count); } - if (unlikely(skb->len < 0)) { - netdev_warn(dev->net, "invalid rx length<0 %d\n", skb->len); - return 0; - } - return 1; } diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index c1d0e7a9da04..a681569ae0b5 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -183,16 +183,22 @@ vmxnet3_get_sset_count(struct net_device *netdev, int sset) } -/* Should be multiple of 4 */ -#define NUM_TX_REGS 8 -#define NUM_RX_REGS 12 - +/* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with + * the version 2 of the vmxnet3 support for ethtool(8) --register-dump. + * Therefore, if any registers are added, removed or modified, then a version + * bump and a corresponding change in the vmxnet3 support for ethtool(8) + * --register-dump would be required. + */ static int vmxnet3_get_regs_len(struct net_device *netdev) { struct vmxnet3_adapter *adapter = netdev_priv(netdev); - return (adapter->num_tx_queues * NUM_TX_REGS * sizeof(u32) + - adapter->num_rx_queues * NUM_RX_REGS * sizeof(u32)); + + return ((9 /* BAR1 registers */ + + (1 + adapter->intr.num_intrs) + + (1 + adapter->num_tx_queues * 17 /* Tx queue registers */) + + (1 + adapter->num_rx_queues * 23 /* Rx queue registers */)) * + sizeof(u32)); } @@ -342,6 +348,12 @@ vmxnet3_get_ethtool_stats(struct net_device *netdev, } +/* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with + * the version 2 of the vmxnet3 support for ethtool(8) --register-dump. + * Therefore, if any registers are added, removed or modified, then a version + * bump and a corresponding change in the vmxnet3 support for ethtool(8) + * --register-dump would be required. + */ static void vmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) { @@ -351,40 +363,90 @@ vmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) memset(p, 0, vmxnet3_get_regs_len(netdev)); - regs->version = 1; + regs->version = 2; /* Update vmxnet3_get_regs_len if we want to dump more registers */ - /* make each ring use multiple of 16 bytes */ - for (i = 0; i < adapter->num_tx_queues; i++) { - buf[j++] = adapter->tx_queue[i].tx_ring.next2fill; - buf[j++] = adapter->tx_queue[i].tx_ring.next2comp; - buf[j++] = adapter->tx_queue[i].tx_ring.gen; - buf[j++] = 0; + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAL); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAH); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACL); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACH); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ICR); + buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ECR); + + buf[j++] = adapter->intr.num_intrs; + for (i = 0; i < adapter->intr.num_intrs; i++) { + buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_IMR + + i * VMXNET3_REG_ALIGN); + } - buf[j++] = adapter->tx_queue[i].comp_ring.next2proc; - buf[j++] = adapter->tx_queue[i].comp_ring.gen; - buf[j++] = adapter->tx_queue[i].stopped; - buf[j++] = 0; + buf[j++] = adapter->num_tx_queues; + for (i = 0; i < adapter->num_tx_queues; i++) { + struct vmxnet3_tx_queue *tq = &adapter->tx_queue[i]; + + buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_TXPROD + + i * VMXNET3_REG_ALIGN); + + buf[j++] = VMXNET3_GET_ADDR_LO(tq->tx_ring.basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(tq->tx_ring.basePA); + buf[j++] = tq->tx_ring.size; + buf[j++] = tq->tx_ring.next2fill; + buf[j++] = tq->tx_ring.next2comp; + buf[j++] = tq->tx_ring.gen; + + buf[j++] = VMXNET3_GET_ADDR_LO(tq->data_ring.basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(tq->data_ring.basePA); + buf[j++] = tq->data_ring.size; + /* transmit data ring buffer size */ + buf[j++] = VMXNET3_HDR_COPY_SIZE; + + buf[j++] = VMXNET3_GET_ADDR_LO(tq->comp_ring.basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(tq->comp_ring.basePA); + buf[j++] = tq->comp_ring.size; + buf[j++] = tq->comp_ring.next2proc; + buf[j++] = tq->comp_ring.gen; + + buf[j++] = tq->stopped; } + buf[j++] = adapter->num_rx_queues; for (i = 0; i < adapter->num_rx_queues; i++) { - buf[j++] = adapter->rx_queue[i].rx_ring[0].next2fill; - buf[j++] = adapter->rx_queue[i].rx_ring[0].next2comp; - buf[j++] = adapter->rx_queue[i].rx_ring[0].gen; + struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i]; + + buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_RXPROD + + i * VMXNET3_REG_ALIGN); + buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_RXPROD2 + + i * VMXNET3_REG_ALIGN); + + buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[0].basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[0].basePA); + buf[j++] = rq->rx_ring[0].size; + buf[j++] = rq->rx_ring[0].next2fill; + buf[j++] = rq->rx_ring[0].next2comp; + buf[j++] = rq->rx_ring[0].gen; + + buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[1].basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[1].basePA); + buf[j++] = rq->rx_ring[1].size; + buf[j++] = rq->rx_ring[1].next2fill; + buf[j++] = rq->rx_ring[1].next2comp; + buf[j++] = rq->rx_ring[1].gen; + + /* receive data ring */ buf[j++] = 0; - - buf[j++] = adapter->rx_queue[i].rx_ring[1].next2fill; - buf[j++] = adapter->rx_queue[i].rx_ring[1].next2comp; - buf[j++] = adapter->rx_queue[i].rx_ring[1].gen; buf[j++] = 0; - - buf[j++] = adapter->rx_queue[i].comp_ring.next2proc; - buf[j++] = adapter->rx_queue[i].comp_ring.gen; buf[j++] = 0; buf[j++] = 0; - } + buf[j++] = VMXNET3_GET_ADDR_LO(rq->comp_ring.basePA); + buf[j++] = VMXNET3_GET_ADDR_HI(rq->comp_ring.basePA); + buf[j++] = rq->comp_ring.size; + buf[j++] = rq->comp_ring.next2proc; + buf[j++] = rq->comp_ring.gen; + } } diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h index 2652245631d1..3f859a55c035 100644 --- a/drivers/net/vmxnet3/vmxnet3_int.h +++ b/drivers/net/vmxnet3/vmxnet3_int.h @@ -69,10 +69,10 @@ /* * Version numbers */ -#define VMXNET3_DRIVER_VERSION_STRING "1.4.2.0-k" +#define VMXNET3_DRIVER_VERSION_STRING "1.4.3.0-k" /* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */ -#define VMXNET3_DRIVER_VERSION_NUM 0x01040200 +#define VMXNET3_DRIVER_VERSION_NUM 0x01040300 #if defined(CONFIG_PCI_MSI) /* RSS only makes sense if MSI-X is supported. */ diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 637e9fd1e14c..191579aeab16 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -34,16 +34,32 @@ #include <net/rtnetlink.h> #include <net/route.h> #include <net/addrconf.h> -#include <net/vrf.h> +#include <net/l3mdev.h> + +#define RT_FL_TOS(oldflp4) \ + ((oldflp4)->flowi4_tos & (IPTOS_RT_MASK | RTO_ONLINK)) #define DRV_NAME "vrf" #define DRV_VERSION "1.0" -#define vrf_is_slave(dev) ((dev)->flags & IFF_SLAVE) - #define vrf_master_get_rcu(dev) \ ((struct net_device *)rcu_dereference(dev->rx_handler_data)) +struct slave { + struct list_head list; + struct net_device *dev; +}; + +struct slave_queue { + struct list_head all_slaves; +}; + +struct net_vrf { + struct slave_queue queue; + struct rtable *rth; + u32 tb_id; +}; + struct pcpu_dstats { u64 tx_pkts; u64 tx_bytes; @@ -58,9 +74,9 @@ static struct dst_entry *vrf_ip_check(struct dst_entry *dst, u32 cookie) return dst; } -static int vrf_ip_local_out(struct sk_buff *skb) +static int vrf_ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) { - return ip_local_out(skb); + return ip_local_out(net, sk, skb); } static unsigned int vrf_v4_mtu(const struct dst_entry *dst) @@ -193,7 +209,8 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb, .flowi4_oif = vrf_dev->ifindex, .flowi4_iif = LOOPBACK_IFINDEX, .flowi4_tos = RT_TOS(ip4h->tos), - .flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_VRFSRC, + .flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_L3MDEV_SRC | + FLOWI_FLAG_SKIP_NH_OIF, .daddr = ip4h->daddr, }; @@ -205,7 +222,7 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb, RT_SCOPE_LINK); } - ret = ip_local_out(skb); + ret = ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb); if (unlikely(net_xmit_eval(ret))) vrf_dev->stats.tx_errors++; else @@ -295,10 +312,9 @@ err: return ret; } -static int vrf_output(struct sock *sk, struct sk_buff *skb) +static int vrf_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct net_device *dev = skb_dst(skb)->dev; - struct net *net = dev_net(dev); IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len); @@ -394,18 +410,15 @@ static void __vrf_insert_slave(struct slave_queue *queue, struct slave *slave) static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) { - struct net_vrf_dev *vrf_ptr = kmalloc(sizeof(*vrf_ptr), GFP_KERNEL); struct slave *slave = kzalloc(sizeof(*slave), GFP_KERNEL); struct net_vrf *vrf = netdev_priv(dev); struct slave_queue *queue = &vrf->queue; int ret = -ENOMEM; - if (!slave || !vrf_ptr) + if (!slave) goto out_fail; slave->dev = port_dev; - vrf_ptr->ifindex = dev->ifindex; - vrf_ptr->tb_id = vrf->tb_id; /* register the packet handler for slave ports */ ret = netdev_rx_handler_register(port_dev, vrf_handle_frame, dev); @@ -420,9 +433,8 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) if (ret < 0) goto out_unregister; - port_dev->flags |= IFF_SLAVE; + port_dev->priv_flags |= IFF_L3MDEV_SLAVE; __vrf_insert_slave(queue, slave); - rcu_assign_pointer(port_dev->vrf_ptr, vrf_ptr); cycle_netdev(port_dev); return 0; @@ -430,14 +442,13 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) out_unregister: netdev_rx_handler_unregister(port_dev); out_fail: - kfree(vrf_ptr); kfree(slave); return ret; } static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev) { - if (netif_is_vrf(port_dev) || vrf_is_slave(port_dev)) + if (netif_is_l3_master(port_dev) || netif_is_l3_slave(port_dev)) return -EINVAL; return do_vrf_add_slave(dev, port_dev); @@ -446,21 +457,15 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev) /* inverse of do_vrf_add_slave */ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev) { - struct net_vrf_dev *vrf_ptr = rtnl_dereference(port_dev->vrf_ptr); struct net_vrf *vrf = netdev_priv(dev); struct slave_queue *queue = &vrf->queue; struct slave *slave; - RCU_INIT_POINTER(port_dev->vrf_ptr, NULL); - netdev_upper_dev_unlink(port_dev, dev); - port_dev->flags &= ~IFF_SLAVE; + port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE; netdev_rx_handler_unregister(port_dev); - /* after netdev_rx_handler_unregister for synchronize_rcu */ - kfree(vrf_ptr); - cycle_netdev(port_dev); slave = __vrf_find_slave_dev(queue, port_dev); @@ -528,6 +533,65 @@ static const struct net_device_ops vrf_netdev_ops = { .ndo_del_slave = vrf_del_slave, }; +static u32 vrf_fib_table(const struct net_device *dev) +{ + struct net_vrf *vrf = netdev_priv(dev); + + return vrf->tb_id; +} + +static struct rtable *vrf_get_rtable(const struct net_device *dev, + const struct flowi4 *fl4) +{ + struct rtable *rth = NULL; + + if (!(fl4->flowi4_flags & FLOWI_FLAG_L3MDEV_SRC)) { + struct net_vrf *vrf = netdev_priv(dev); + + rth = vrf->rth; + atomic_inc(&rth->dst.__refcnt); + } + + return rth; +} + +/* called under rcu_read_lock */ +static void vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4) +{ + struct fib_result res = { .tclassid = 0 }; + struct net *net = dev_net(dev); + u32 orig_tos = fl4->flowi4_tos; + u8 flags = fl4->flowi4_flags; + u8 scope = fl4->flowi4_scope; + u8 tos = RT_FL_TOS(fl4); + + if (unlikely(!fl4->daddr)) + return; + + fl4->flowi4_flags |= FLOWI_FLAG_SKIP_NH_OIF; + fl4->flowi4_iif = LOOPBACK_IFINDEX; + fl4->flowi4_tos = tos & IPTOS_RT_MASK; + fl4->flowi4_scope = ((tos & RTO_ONLINK) ? + RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); + + if (!fib_lookup(net, fl4, &res, 0)) { + if (res.type == RTN_LOCAL) + fl4->saddr = res.fi->fib_prefsrc ? : fl4->daddr; + else + fib_select_path(net, &res, fl4, -1); + } + + fl4->flowi4_flags = flags; + fl4->flowi4_tos = orig_tos; + fl4->flowi4_scope = scope; +} + +static const struct l3mdev_ops vrf_l3mdev_ops = { + .l3mdev_fib_table = vrf_fib_table, + .l3mdev_get_rtable = vrf_get_rtable, + .l3mdev_get_saddr = vrf_get_saddr, +}; + static void vrf_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { @@ -545,6 +609,7 @@ static void vrf_setup(struct net_device *dev) /* Initialize the device structure. */ dev->netdev_ops = &vrf_netdev_ops; + dev->l3mdev_ops = &vrf_l3mdev_ops; dev->ethtool_ops = &vrf_ethtool_ops; dev->destructor = free_netdev; @@ -571,10 +636,6 @@ static int vrf_validate(struct nlattr *tb[], struct nlattr *data[]) static void vrf_dellink(struct net_device *dev, struct list_head *head) { - struct net_vrf_dev *vrf_ptr = rtnl_dereference(dev->vrf_ptr); - - RCU_INIT_POINTER(dev->vrf_ptr, NULL); - kfree_rcu(vrf_ptr, rcu); unregister_netdevice_queue(dev, head); } @@ -582,7 +643,6 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct net_vrf *vrf = netdev_priv(dev); - struct net_vrf_dev *vrf_ptr; int err; if (!data || !data[IFLA_VRF_TABLE]) @@ -590,26 +650,15 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev, vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]); - dev->priv_flags |= IFF_VRF_MASTER; - - err = -ENOMEM; - vrf_ptr = kmalloc(sizeof(*dev->vrf_ptr), GFP_KERNEL); - if (!vrf_ptr) - goto out_fail; - - vrf_ptr->ifindex = dev->ifindex; - vrf_ptr->tb_id = vrf->tb_id; + dev->priv_flags |= IFF_L3MDEV_MASTER; err = register_netdevice(dev); if (err < 0) goto out_fail; - rcu_assign_pointer(dev->vrf_ptr, vrf_ptr); - return 0; out_fail: - kfree(vrf_ptr); free_netdev(dev); return err; } @@ -653,10 +702,9 @@ static int vrf_device_event(struct notifier_block *unused, /* only care about unregister events to drop slave references */ if (event == NETDEV_UNREGISTER) { - struct net_vrf_dev *vrf_ptr = rtnl_dereference(dev->vrf_ptr); struct net_device *vrf_dev; - if (!vrf_ptr || netif_is_vrf(dev)) + if (!netif_is_l3_slave(dev)) goto out; vrf_dev = netdev_master_upper_dev_get(dev); diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index cf8b7f0473b3..ce704df7681b 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -75,8 +75,7 @@ static struct rtnl_link_ops vxlan_link_ops; static const u8 all_zeros_mac[ETH_ALEN]; -static struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, - bool no_share, u32 flags); +static int vxlan_sock_add(struct vxlan_dev *vxlan); /* per-network namespace private data for this module */ struct vxlan_net { @@ -994,19 +993,30 @@ static bool vxlan_snoop(struct net_device *dev, static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev) { struct vxlan_dev *vxlan; + unsigned short family = dev->default_dst.remote_ip.sa.sa_family; /* The vxlan_sock is only used by dev, leaving group has * no effect on other vxlan devices. */ - if (atomic_read(&dev->vn_sock->refcnt) == 1) + if (family == AF_INET && dev->vn4_sock && + atomic_read(&dev->vn4_sock->refcnt) == 1) return false; +#if IS_ENABLED(CONFIG_IPV6) + if (family == AF_INET6 && dev->vn6_sock && + atomic_read(&dev->vn6_sock->refcnt) == 1) + return false; +#endif list_for_each_entry(vxlan, &vn->vxlan_list, next) { if (!netif_running(vxlan->dev) || vxlan == dev) continue; - if (vxlan->vn_sock != dev->vn_sock) + if (family == AF_INET && vxlan->vn4_sock != dev->vn4_sock) + continue; +#if IS_ENABLED(CONFIG_IPV6) + if (family == AF_INET6 && vxlan->vn6_sock != dev->vn6_sock) continue; +#endif if (!vxlan_addr_equal(&vxlan->default_dst.remote_ip, &dev->default_dst.remote_ip)) @@ -1022,15 +1032,16 @@ static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev) return false; } -static void vxlan_sock_release(struct vxlan_sock *vs) +static void __vxlan_sock_release(struct vxlan_sock *vs) { - struct sock *sk = vs->sock->sk; - struct net *net = sock_net(sk); - struct vxlan_net *vn = net_generic(net, vxlan_net_id); + struct vxlan_net *vn; + if (!vs) + return; if (!atomic_dec_and_test(&vs->refcnt)) return; + vn = net_generic(sock_net(vs->sock->sk), vxlan_net_id); spin_lock(&vn->sock_lock); hlist_del_rcu(&vs->hlist); vxlan_notify_del_rx_port(vs); @@ -1039,32 +1050,43 @@ static void vxlan_sock_release(struct vxlan_sock *vs) queue_work(vxlan_wq, &vs->del_work); } +static void vxlan_sock_release(struct vxlan_dev *vxlan) +{ + __vxlan_sock_release(vxlan->vn4_sock); +#if IS_ENABLED(CONFIG_IPV6) + __vxlan_sock_release(vxlan->vn6_sock); +#endif +} + /* Update multicast group membership when first VNI on * multicast address is brought up */ static int vxlan_igmp_join(struct vxlan_dev *vxlan) { - struct vxlan_sock *vs = vxlan->vn_sock; - struct sock *sk = vs->sock->sk; + struct sock *sk; union vxlan_addr *ip = &vxlan->default_dst.remote_ip; int ifindex = vxlan->default_dst.remote_ifindex; int ret = -EINVAL; - lock_sock(sk); if (ip->sa.sa_family == AF_INET) { struct ip_mreqn mreq = { .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr, .imr_ifindex = ifindex, }; + sk = vxlan->vn4_sock->sock->sk; + lock_sock(sk); ret = ip_mc_join_group(sk, &mreq); + release_sock(sk); #if IS_ENABLED(CONFIG_IPV6) } else { + sk = vxlan->vn6_sock->sock->sk; + lock_sock(sk); ret = ipv6_stub->ipv6_sock_mc_join(sk, ifindex, &ip->sin6.sin6_addr); + release_sock(sk); #endif } - release_sock(sk); return ret; } @@ -1072,27 +1094,30 @@ static int vxlan_igmp_join(struct vxlan_dev *vxlan) /* Inverse of vxlan_igmp_join when last VNI is brought down */ static int vxlan_igmp_leave(struct vxlan_dev *vxlan) { - struct vxlan_sock *vs = vxlan->vn_sock; - struct sock *sk = vs->sock->sk; + struct sock *sk; union vxlan_addr *ip = &vxlan->default_dst.remote_ip; int ifindex = vxlan->default_dst.remote_ifindex; int ret = -EINVAL; - lock_sock(sk); if (ip->sa.sa_family == AF_INET) { struct ip_mreqn mreq = { .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr, .imr_ifindex = ifindex, }; + sk = vxlan->vn4_sock->sock->sk; + lock_sock(sk); ret = ip_mc_leave_group(sk, &mreq); + release_sock(sk); #if IS_ENABLED(CONFIG_IPV6) } else { + sk = vxlan->vn6_sock->sock->sk; + lock_sock(sk); ret = ipv6_stub->ipv6_sock_mc_drop(sk, ifindex, &ip->sin6.sin6_addr); + release_sock(sk); #endif } - release_sock(sk); return ret; } @@ -1873,8 +1898,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, { struct ip_tunnel_info *info; struct vxlan_dev *vxlan = netdev_priv(dev); - struct sock *sk = vxlan->vn_sock->sock->sk; - unsigned short family = vxlan_get_sk_family(vxlan->vn_sock); + struct sock *sk; struct rtable *rt = NULL; const struct iphdr *old_iph; struct flowi4 fl4; @@ -1901,13 +1925,10 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, dev->name); goto drop; } - if (family != ip_tunnel_info_af(info)) - goto drop; - dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port; vni = be64_to_cpu(info->key.tun_id); - remote_ip.sa.sa_family = family; - if (family == AF_INET) + remote_ip.sa.sa_family = ip_tunnel_info_af(info); + if (remote_ip.sa.sa_family == AF_INET) remote_ip.sin.sin_addr.s_addr = info->key.u.ipv4.dst; else remote_ip.sin6.sin6_addr = info->key.u.ipv6.dst; @@ -1952,6 +1973,10 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, } if (dst->sa.sa_family == AF_INET) { + if (!vxlan->vn4_sock) + goto drop; + sk = vxlan->vn4_sock->sock->sk; + if (info && (info->key.tun_flags & TUNNEL_DONT_FRAGMENT)) df = htons(IP_DF); @@ -2013,6 +2038,10 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct flowi6 fl6; u32 rt6i_flags; + if (!vxlan->vn6_sock) + goto drop; + sk = vxlan->vn6_sock->sock->sk; + memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_oif = rdst ? rdst->remote_ifindex : 0; fl6.daddr = dst->sin6.sin6_addr; @@ -2204,7 +2233,6 @@ static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan) struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); __u32 vni = vxlan->default_dst.remote_vni; - vxlan->vn_sock = vs; spin_lock(&vn->sock_lock); hlist_add_head_rcu(&vxlan->hlist, vni_head(vs, vni)); spin_unlock(&vn->sock_lock); @@ -2244,22 +2272,18 @@ static void vxlan_uninit(struct net_device *dev) static int vxlan_open(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_sock *vs; - int ret = 0; + int ret; - vs = vxlan_sock_add(vxlan->net, vxlan->cfg.dst_port, - vxlan->cfg.no_share, vxlan->flags); - if (IS_ERR(vs)) - return PTR_ERR(vs); - - vxlan_vs_add_dev(vs, vxlan); + ret = vxlan_sock_add(vxlan); + if (ret < 0) + return ret; if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) { ret = vxlan_igmp_join(vxlan); if (ret == -EADDRINUSE) ret = 0; if (ret) { - vxlan_sock_release(vs); + vxlan_sock_release(vxlan); return ret; } } @@ -2294,7 +2318,6 @@ static int vxlan_stop(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); - struct vxlan_sock *vs = vxlan->vn_sock; int ret = 0; if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip) && @@ -2304,7 +2327,7 @@ static int vxlan_stop(struct net_device *dev) del_timer_sync(&vxlan->age_timer); vxlan_flush(vxlan); - vxlan_sock_release(vs); + vxlan_sock_release(vxlan); return ret; } @@ -2392,10 +2415,6 @@ static void vxlan_setup(struct net_device *dev) eth_hw_addr_random(dev); ether_setup(dev); - if (vxlan->default_dst.remote_ip.sa.sa_family == AF_INET6) - dev->needed_headroom = ETH_HLEN + VXLAN6_HEADROOM; - else - dev->needed_headroom = ETH_HLEN + VXLAN_HEADROOM; dev->netdev_ops = &vxlan_netdev_ops; dev->destructor = free_netdev; @@ -2544,14 +2563,13 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6, } /* Create new listen socket if needed */ -static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, - u32 flags) +static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6, + __be16 port, u32 flags) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_sock *vs; struct socket *sock; unsigned int h; - bool ipv6 = !!(flags & VXLAN_F_IPV6); struct udp_tunnel_sock_cfg tunnel_cfg; vs = kzalloc(sizeof(*vs), GFP_KERNEL); @@ -2596,27 +2614,53 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, return vs; } -static struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, - bool no_share, u32 flags) +static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6) { - struct vxlan_net *vn = net_generic(net, vxlan_net_id); - struct vxlan_sock *vs; - bool ipv6 = flags & VXLAN_F_IPV6; + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); + struct vxlan_sock *vs = NULL; - if (!no_share) { + if (!vxlan->cfg.no_share) { spin_lock(&vn->sock_lock); - vs = vxlan_find_sock(net, ipv6 ? AF_INET6 : AF_INET, port, - flags); - if (vs) { - if (!atomic_add_unless(&vs->refcnt, 1, 0)) - vs = ERR_PTR(-EBUSY); + vs = vxlan_find_sock(vxlan->net, ipv6 ? AF_INET6 : AF_INET, + vxlan->cfg.dst_port, vxlan->flags); + if (vs && !atomic_add_unless(&vs->refcnt, 1, 0)) { spin_unlock(&vn->sock_lock); - return vs; + return -EBUSY; } spin_unlock(&vn->sock_lock); } + if (!vs) + vs = vxlan_socket_create(vxlan->net, ipv6, + vxlan->cfg.dst_port, vxlan->flags); + if (IS_ERR(vs)) + return PTR_ERR(vs); +#if IS_ENABLED(CONFIG_IPV6) + if (ipv6) + vxlan->vn6_sock = vs; + else +#endif + vxlan->vn4_sock = vs; + vxlan_vs_add_dev(vs, vxlan); + return 0; +} - return vxlan_socket_create(net, port, flags); +static int vxlan_sock_add(struct vxlan_dev *vxlan) +{ + bool ipv6 = vxlan->flags & VXLAN_F_IPV6; + bool metadata = vxlan->flags & VXLAN_F_COLLECT_METADATA; + int ret = 0; + + vxlan->vn4_sock = NULL; +#if IS_ENABLED(CONFIG_IPV6) + vxlan->vn6_sock = NULL; + if (ipv6 || metadata) + ret = __vxlan_sock_add(vxlan, true); +#endif + if (!ret && (!ipv6 || metadata)) + ret = __vxlan_sock_add(vxlan, false); + if (ret < 0) + vxlan_sock_release(vxlan); + return ret; } static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, @@ -2625,6 +2669,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, struct vxlan_net *vn = net_generic(src_net, vxlan_net_id); struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_rdst *dst = &vxlan->default_dst; + unsigned short needed_headroom = ETH_HLEN; int err; bool use_ipv6 = false; __be16 default_port = vxlan->cfg.dst_port; @@ -2640,8 +2685,12 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, dst->remote_ip.sa.sa_family = AF_INET; if (dst->remote_ip.sa.sa_family == AF_INET6 || - vxlan->cfg.saddr.sa.sa_family == AF_INET6) + vxlan->cfg.saddr.sa.sa_family == AF_INET6) { + if (!IS_ENABLED(CONFIG_IPV6)) + return -EPFNOSUPPORT; use_ipv6 = true; + vxlan->flags |= VXLAN_F_IPV6; + } if (conf->remote_ifindex) { struct net_device *lowerdev @@ -2661,17 +2710,20 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, pr_info("IPv6 is disabled via sysctl\n"); return -EPERM; } - vxlan->flags |= VXLAN_F_IPV6; } #endif if (!conf->mtu) dev->mtu = lowerdev->mtu - (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM); - dev->needed_headroom = lowerdev->hard_header_len + - (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM); - } else if (use_ipv6) - vxlan->flags |= VXLAN_F_IPV6; + needed_headroom = lowerdev->hard_header_len; + } + + if (use_ipv6 || conf->flags & VXLAN_F_COLLECT_METADATA) + needed_headroom += VXLAN6_HEADROOM; + else + needed_headroom += VXLAN_HEADROOM; + dev->needed_headroom = needed_headroom; memcpy(&vxlan->cfg, conf, sizeof(*conf)); if (!vxlan->cfg.dst_port) diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index d0c97c220026..8ae838d96a9e 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1237,6 +1237,7 @@ struct airo_info { int wep_capable; int max_wep_idx; + int last_auth; /* WPA-related stuff */ unsigned int bssListFirst; @@ -3266,6 +3267,7 @@ static void airo_handle_link(struct airo_info *ai) wake_up_interruptible(&ai->thr_wait); } else airo_send_event(ai->dev); + netif_carrier_on(ai->dev); } else if (!scan_forceloss) { if (auto_wep && !ai->expires) { ai->expires = RUN_AT(3*HZ); @@ -3276,6 +3278,9 @@ static void airo_handle_link(struct airo_info *ai) eth_zero_addr(wrqu.ap_addr.sa_data); wrqu.ap_addr.sa_family = ARPHRD_ETHER; wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL); + netif_carrier_off(ai->dev); + } else { + netif_carrier_off(ai->dev); } } @@ -3612,6 +3617,7 @@ static void disable_MAC( struct airo_info *ai, int lock ) { return; if (test_bit(FLAG_ENABLED, &ai->flags)) { + netif_carrier_off(ai->dev); memset(&cmd, 0, sizeof(cmd)); cmd.cmd = MAC_DISABLE; // disable in case already enabled issuecommand(ai, &cmd, &rsp); @@ -3786,6 +3792,16 @@ badrx: } } +static inline void set_auth_type(struct airo_info *local, int auth_type) +{ + local->config.authType = auth_type; + /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT). + * Used by airo_set_auth() + */ + if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT) + local->last_auth = auth_type; +} + static u16 setup_card(struct airo_info *ai, u8 *mac, int lock) { Cmd cmd; @@ -3862,7 +3878,7 @@ static u16 setup_card(struct airo_info *ai, u8 *mac, int lock) "level scale"); } ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS; - ai->config.authType = AUTH_OPEN; + set_auth_type(ai, AUTH_OPEN); ai->config.modulation = MOD_CCK; if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) && @@ -4880,13 +4896,13 @@ static void proc_config_on_close(struct inode *inode, struct file *file) line += 5; switch( line[0] ) { case 's': - ai->config.authType = AUTH_SHAREDKEY; + set_auth_type(ai, AUTH_SHAREDKEY); break; case 'e': - ai->config.authType = AUTH_ENCRYPT; + set_auth_type(ai, AUTH_ENCRYPT); break; default: - ai->config.authType = AUTH_OPEN; + set_auth_type(ai, AUTH_OPEN); break; } set_bit (FLAG_COMMIT, &ai->flags); @@ -6368,9 +6384,8 @@ static int airo_set_encode(struct net_device *dev, * should be enabled (user may turn it off later) * This is also how "iwconfig ethX key on" works */ if((index == current_index) && (key.len > 0) && - (local->config.authType == AUTH_OPEN)) { - local->config.authType = AUTH_ENCRYPT; - } + (local->config.authType == AUTH_OPEN)) + set_auth_type(local, AUTH_ENCRYPT); } else { /* Do we want to just set the transmit key index ? */ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; @@ -6389,12 +6404,12 @@ static int airo_set_encode(struct net_device *dev, } } /* Read the flags */ - if(dwrq->flags & IW_ENCODE_DISABLED) - local->config.authType = AUTH_OPEN; // disable encryption + if (dwrq->flags & IW_ENCODE_DISABLED) + set_auth_type(local, AUTH_OPEN); /* disable encryption */ if(dwrq->flags & IW_ENCODE_RESTRICTED) - local->config.authType = AUTH_SHAREDKEY; // Only Both - if(dwrq->flags & IW_ENCODE_OPEN) - local->config.authType = AUTH_ENCRYPT; // Only Wep + set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ + if (dwrq->flags & IW_ENCODE_OPEN) + set_auth_type(local, AUTH_ENCRYPT); /* Only Wep */ /* Commit the changes to flags if needed */ if (local->config.authType != currentAuthType) set_bit (FLAG_COMMIT, &local->flags); @@ -6549,12 +6564,12 @@ static int airo_set_encodeext(struct net_device *dev, } /* Read the flags */ - if(encoding->flags & IW_ENCODE_DISABLED) - local->config.authType = AUTH_OPEN; // disable encryption + if (encoding->flags & IW_ENCODE_DISABLED) + set_auth_type(local, AUTH_OPEN); /* disable encryption */ if(encoding->flags & IW_ENCODE_RESTRICTED) - local->config.authType = AUTH_SHAREDKEY; // Only Both - if(encoding->flags & IW_ENCODE_OPEN) - local->config.authType = AUTH_ENCRYPT; // Only Wep + set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ + if (encoding->flags & IW_ENCODE_OPEN) + set_auth_type(local, AUTH_ENCRYPT); /* Commit the changes to flags if needed */ if (local->config.authType != currentAuthType) set_bit (FLAG_COMMIT, &local->flags); @@ -6659,9 +6674,9 @@ static int airo_set_auth(struct net_device *dev, if (param->value) { /* Only change auth type if unencrypted */ if (currentAuthType == AUTH_OPEN) - local->config.authType = AUTH_ENCRYPT; + set_auth_type(local, AUTH_ENCRYPT); } else { - local->config.authType = AUTH_OPEN; + set_auth_type(local, AUTH_OPEN); } /* Commit the changes to flags if needed */ @@ -6670,13 +6685,14 @@ static int airo_set_auth(struct net_device *dev, break; case IW_AUTH_80211_AUTH_ALG: { - /* FIXME: What about AUTH_OPEN? This API seems to - * disallow setting our auth to AUTH_OPEN. - */ if (param->value & IW_AUTH_ALG_SHARED_KEY) { - local->config.authType = AUTH_SHAREDKEY; + set_auth_type(local, AUTH_SHAREDKEY); } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { - local->config.authType = AUTH_ENCRYPT; + /* We don't know here if WEP open system or + * unencrypted mode was requested - so use the + * last mode (of these two) used last time + */ + set_auth_type(local, local->last_auth); } else return -EINVAL; diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index b87b98617073..879625adc63a 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -34,16 +34,19 @@ unsigned int ath10k_debug_mask; static unsigned int ath10k_cryptmode_param; static bool uart_print; static bool skip_otp; +static bool rawmode; module_param_named(debug_mask, ath10k_debug_mask, uint, 0644); module_param_named(cryptmode, ath10k_cryptmode_param, uint, 0644); module_param(uart_print, bool, 0644); module_param(skip_otp, bool, 0644); +module_param(rawmode, bool, 0644); MODULE_PARM_DESC(debug_mask, "Debugging mask"); MODULE_PARM_DESC(uart_print, "Uart target debugging"); MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode"); MODULE_PARM_DESC(cryptmode, "Crypto mode: 0-hardware, 1-software"); +MODULE_PARM_DESC(rawmode, "Use raw 802.11 frame datapath"); static const struct ath10k_hw_params ath10k_hw_params_list[] = { { @@ -54,6 +57,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .has_shifted_cc_wraparound = true, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { .dir = QCA988X_HW_2_0_FW_DIR, .fw = QCA988X_HW_2_0_FW_FILE, @@ -70,6 +74,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .uart_pin = 6, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { .dir = QCA6174_HW_2_1_FW_DIR, .fw = QCA6174_HW_2_1_FW_FILE, @@ -86,6 +91,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .uart_pin = 6, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { .dir = QCA6174_HW_3_0_FW_DIR, .fw = QCA6174_HW_3_0_FW_FILE, @@ -102,6 +108,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .uart_pin = 6, .otp_exe_param = 0, .channel_counters_freq_hz = 88000, + .max_probe_resp_desc_thres = 0, .fw = { /* uses same binaries as hw3.0 */ .dir = QCA6174_HW_3_0_FW_DIR, @@ -120,6 +127,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .otp_exe_param = 0x00000700, .continuous_frag_desc = true, .channel_counters_freq_hz = 150000, + .max_probe_resp_desc_thres = 24, .fw = { .dir = QCA99X0_HW_2_0_FW_DIR, .fw = QCA99X0_HW_2_0_FW_FILE, @@ -142,12 +150,17 @@ static const char *const ath10k_core_fw_feature_str[] = { [ATH10K_FW_FEATURE_IGNORE_OTP_RESULT] = "ignore-otp", [ATH10K_FW_FEATURE_NO_NWIFI_DECAP_4ADDR_PADDING] = "no-4addr-pad", [ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT] = "skip-clock-init", + [ATH10K_FW_FEATURE_RAW_MODE_SUPPORT] = "raw-mode", }; static unsigned int ath10k_core_get_fw_feature_str(char *buf, size_t buf_len, enum ath10k_fw_features feat) { + /* make sure that ath10k_core_fw_feature_str[] gets updated */ + BUILD_BUG_ON(ARRAY_SIZE(ath10k_core_fw_feature_str) != + ATH10K_FW_FEATURE_COUNT); + if (feat >= ARRAY_SIZE(ath10k_core_fw_feature_str) || WARN_ON(!ath10k_core_fw_feature_str[feat])) { return scnprintf(buf, buf_len, "bit%d", feat); @@ -1117,6 +1130,15 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) ar->htt.max_num_amsdu = ATH10K_HTT_MAX_NUM_AMSDU_DEFAULT; ar->htt.max_num_ampdu = ATH10K_HTT_MAX_NUM_AMPDU_DEFAULT; + if (rawmode) { + if (!test_bit(ATH10K_FW_FEATURE_RAW_MODE_SUPPORT, + ar->fw_features)) { + ath10k_err(ar, "rawmode = 1 requires support from firmware"); + return -EINVAL; + } + set_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags); + } + if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { ar->wmi.rx_decap_mode = ATH10K_HW_TXRX_RAW; @@ -1714,6 +1736,7 @@ void ath10k_core_destroy(struct ath10k *ar) destroy_workqueue(ar->workqueue_aux); ath10k_debug_destroy(ar); + ath10k_wmi_free_host_mem(ar); ath10k_mac_destroy(ar); } EXPORT_SYMBOL(ath10k_core_destroy); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 12542144fe12..04e040a06cb1 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -612,6 +612,11 @@ struct ath10k { u32 channel_counters_freq_hz; + /* Mgmt tx descriptors threshold for limiting probe response + * frames. + */ + u32 max_probe_resp_desc_thres; + struct ath10k_hw_params_fw { const char *dir; const char *fw; diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 573187512895..5a8e4eae7a9c 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1485,6 +1485,7 @@ struct ath10k_htt { spinlock_t tx_lock; int max_num_pending_tx; int num_pending_tx; + int num_pending_mgmt_tx; struct idr pending_tx; wait_queue_head_t empty_tx_wq; struct dma_pool *tx_pool; @@ -1587,7 +1588,7 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, u8 max_subfrms_ampdu, u8 max_subfrms_amsdu); -void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt); +void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc); int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb); void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id); int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *); diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 1b7a04366256..606c1a34f004 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -643,6 +643,8 @@ struct amsdu_subframe_hdr { __be16 len; } __packed; +#define GROUP_ID_IS_SU_MIMO(x) ((x) == 0 || (x) == 63) + static void ath10k_htt_rx_h_rates(struct ath10k *ar, struct ieee80211_rx_status *status, struct htt_rx_desc *rxd) @@ -650,6 +652,7 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, struct ieee80211_supported_band *sband; u8 cck, rate, bw, sgi, mcs, nss; u8 preamble = 0; + u8 group_id; u32 info1, info2, info3; info1 = __le32_to_cpu(rxd->ppdu_start.info1); @@ -692,10 +695,50 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, case HTT_RX_VHT_WITH_TXBF: /* VHT-SIG-A1 in info2, VHT-SIG-A2 in info3 TODO check this */ - mcs = (info3 >> 4) & 0x0F; - nss = ((info2 >> 10) & 0x07) + 1; bw = info2 & 3; sgi = info3 & 1; + group_id = (info2 >> 4) & 0x3F; + + if (GROUP_ID_IS_SU_MIMO(group_id)) { + mcs = (info3 >> 4) & 0x0F; + nss = ((info2 >> 10) & 0x07) + 1; + } else { + /* Hardware doesn't decode VHT-SIG-B into Rx descriptor + * so it's impossible to decode MCS. Also since + * firmware consumes Group Id Management frames host + * has no knowledge regarding group/user position + * mapping so it's impossible to pick the correct Nsts + * from VHT-SIG-A1. + * + * Bandwidth and SGI are valid so report the rateinfo + * on best-effort basis. + */ + mcs = 0; + nss = 1; + } + + if (mcs > 0x09) { + ath10k_warn(ar, "invalid MCS received %u\n", mcs); + ath10k_warn(ar, "rxd %08x mpdu start %08x %08x msdu start %08x %08x ppdu start %08x %08x %08x %08x %08x\n", + __le32_to_cpu(rxd->attention.flags), + __le32_to_cpu(rxd->mpdu_start.info0), + __le32_to_cpu(rxd->mpdu_start.info1), + __le32_to_cpu(rxd->msdu_start.common.info0), + __le32_to_cpu(rxd->msdu_start.common.info1), + rxd->ppdu_start.info0, + __le32_to_cpu(rxd->ppdu_start.info1), + __le32_to_cpu(rxd->ppdu_start.info2), + __le32_to_cpu(rxd->ppdu_start.info3), + __le32_to_cpu(rxd->ppdu_start.info4)); + + ath10k_warn(ar, "msdu end %08x mpdu end %08x\n", + __le32_to_cpu(rxd->msdu_end.common.info0), + __le32_to_cpu(rxd->mpdu_end.info0)); + + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, + "rx desc msdu payload: ", + rxd->msdu_payload, 50); + } status->rate_idx = mcs; status->vht_nss = nss; diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 43aa5e2d1b87..eb5ba9bb8b4d 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -22,22 +22,28 @@ #include "txrx.h" #include "debug.h" -void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt) +void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc) { + if (limit_mgmt_desc) + htt->num_pending_mgmt_tx--; + htt->num_pending_tx--; if (htt->num_pending_tx == htt->max_num_pending_tx - 1) ath10k_mac_tx_unlock(htt->ar, ATH10K_TX_PAUSE_Q_FULL); } -static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt) +static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, + bool limit_mgmt_desc) { spin_lock_bh(&htt->tx_lock); - __ath10k_htt_tx_dec_pending(htt); + __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); spin_unlock_bh(&htt->tx_lock); } -static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt) +static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt, + bool limit_mgmt_desc, bool is_probe_resp) { + struct ath10k *ar = htt->ar; int ret = 0; spin_lock_bh(&htt->tx_lock); @@ -47,6 +53,15 @@ static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt) goto exit; } + if (limit_mgmt_desc) { + if (is_probe_resp && (htt->num_pending_mgmt_tx > + ar->hw_params.max_probe_resp_desc_thres)) { + ret = -EBUSY; + goto exit; + } + htt->num_pending_mgmt_tx++; + } + htt->num_pending_tx++; if (htt->num_pending_tx == htt->max_num_pending_tx) ath10k_mac_tx_lock(htt->ar, ATH10K_TX_PAUSE_Q_FULL); @@ -417,8 +432,19 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) int len = 0; int msdu_id = -1; int res; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + bool limit_mgmt_desc = false; + bool is_probe_resp = false; + + if (ar->hw_params.max_probe_resp_desc_thres) { + limit_mgmt_desc = true; + + if (ieee80211_is_probe_resp(hdr->frame_control)) + is_probe_resp = true; + } + + res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp); - res = ath10k_htt_tx_inc_pending(htt); if (res) goto err; @@ -476,7 +502,7 @@ err_free_msdu_id: ath10k_htt_tx_free_msdu_id(htt, msdu_id); spin_unlock_bh(&htt->tx_lock); err_tx_dec: - ath10k_htt_tx_dec_pending(htt); + ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); err: return res; } @@ -498,8 +524,18 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) dma_addr_t paddr = 0; u32 frags_paddr = 0; struct htt_msdu_ext_desc *ext_desc = NULL; + bool limit_mgmt_desc = false; + bool is_probe_resp = false; + + if (unlikely(ieee80211_is_mgmt(hdr->frame_control)) && + ar->hw_params.max_probe_resp_desc_thres) { + limit_mgmt_desc = true; + + if (ieee80211_is_probe_resp(hdr->frame_control)) + is_probe_resp = true; + } - res = ath10k_htt_tx_inc_pending(htt); + res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp); if (res) goto err; @@ -528,7 +564,8 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) ieee80211_has_protected(hdr->frame_control)) { skb_put(msdu, IEEE80211_CCMP_MIC_LEN); } else if (!skb_cb->htt.nohwcrypt && - skb_cb->txmode == ATH10K_HW_TXRX_RAW) { + skb_cb->txmode == ATH10K_HW_TXRX_RAW && + ieee80211_has_protected(hdr->frame_control)) { skb_put(msdu, IEEE80211_CCMP_MIC_LEN); } @@ -678,7 +715,7 @@ err_free_msdu_id: ath10k_htt_tx_free_msdu_id(htt, msdu_id); spin_unlock_bh(&htt->tx_lock); err_tx_dec: - ath10k_htt_tx_dec_pending(htt); + ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); err: return res; } diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 23afcda2de96..bc421a5c5356 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -414,16 +414,6 @@ enum ath10k_hw_rate_cck { #define CE_COUNT ar->hw_values->ce_count /* - * Total number of PCIe MSI interrupts requested for all interrupt sources. - * PCIe standard forces this to be a power of 2. - * Some Host OS's limit MSI requests that can be granted to 8 - * so for now we abide by this limit and avoid requesting more - * than that. - */ -#define MSI_NUM_REQUEST_LOG2 3 -#define MSI_NUM_REQUEST (1<<MSI_NUM_REQUEST_LOG2) - -/* * Granted MSIs are assigned as follows: * Firmware uses the first * Remaining MSIs, if any, are used by Copy Engines diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 64674c955d44..79490ad41ac5 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1070,6 +1070,7 @@ static bool ath10k_mac_monitor_vdev_is_needed(struct ath10k *ar) return false; return ar->monitor || + ar->filter_flags & FIF_OTHER_BSS || test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags); } @@ -3617,9 +3618,6 @@ static int ath10k_start_scan(struct ath10k *ar, } spin_unlock_bh(&ar->data_lock); - /* Add a 200ms margin to account for event/command processing */ - ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, - msecs_to_jiffies(arg->max_scan_time+200)); return 0; } @@ -4064,21 +4062,56 @@ static u32 get_nss_from_chainmask(u16 chain_mask) return 1; } +static int ath10k_mac_get_vht_cap_bf_sts(struct ath10k *ar) +{ + int nsts = ar->vht_cap_info; + nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; + nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; + + /* If firmware does not deliver to host number of space-time + * streams supported, assume it support up to 4 BF STS and return + * the value for VHT CAP: nsts-1) + * */ + if (nsts == 0) + return 3; + + return nsts; +} + +static int ath10k_mac_get_vht_cap_bf_sound_dim(struct ath10k *ar) +{ + int sound_dim = ar->vht_cap_info; + sound_dim &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; + sound_dim >>=IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; + + /* If the sounding dimension is not advertised by the firmware, + * let's use a default value of 1 + */ + if (sound_dim == 0) + return 1; + + return sound_dim; +} + static int ath10k_mac_set_txbf_conf(struct ath10k_vif *arvif) { u32 value = 0; struct ath10k *ar = arvif->ar; + int nsts; + int sound_dim; if (ath10k_wmi_get_txbf_conf_scheme(ar) != WMI_TXBF_CONF_BEFORE_ASSOC) return 0; + nsts = ath10k_mac_get_vht_cap_bf_sts(ar); if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) - value |= SM((ar->num_rf_chains - 1), WMI_TXBF_STS_CAP_OFFSET); + value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET); + sound_dim = ath10k_mac_get_vht_cap_bf_sound_dim(ar); if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) - value |= SM((ar->num_rf_chains - 1), WMI_BF_SOUND_DIM_OFFSET); + value |= SM(sound_dim, WMI_BF_SOUND_DIM_OFFSET); if (!value) return 0; @@ -4175,6 +4208,14 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_ADHOC: arvif->vdev_type = WMI_VDEV_TYPE_IBSS; break; + case NL80211_IFTYPE_MESH_POINT: + if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { + ret = -EINVAL; + ath10k_warn(ar, "must load driver with rawmode=1 to add mesh interfaces\n"); + goto err; + } + arvif->vdev_type = WMI_VDEV_TYPE_AP; + break; case NL80211_IFTYPE_AP: arvif->vdev_type = WMI_VDEV_TYPE_AP; @@ -4215,6 +4256,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, * become corrupted, e.g. have garbled IEs or out-of-date TIM bitmap. */ if (vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_AP) { arvif->beacon_buf = dma_zalloc_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN, @@ -4554,6 +4596,13 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, if (ret) ath10k_warn(ar, "failed to update beacon template: %d\n", ret); + + if (ieee80211_vif_is_mesh(vif)) { + /* mesh doesn't use SSID but firmware needs it */ + strncpy(arvif->u.ap.ssid, "mesh", + sizeof(arvif->u.ap.ssid)); + arvif->u.ap.ssid_len = 4; + } } if (changed & BSS_CHANGED_AP_PROBE_RESP) { @@ -4751,6 +4800,11 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, spin_unlock_bh(&ar->data_lock); } + /* Add a 200ms margin to account for event/command processing */ + ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, + msecs_to_jiffies(arg.max_scan_time + + 200)); + exit: mutex_unlock(&ar->conf_mutex); return ret; @@ -5293,6 +5347,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC && (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_ADHOC)) { /* * New association. @@ -5328,6 +5383,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH && (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || vif->type == NL80211_IFTYPE_ADHOC)) { /* * Disassociation. @@ -5901,7 +5957,7 @@ ath10k_mac_bitrate_mask_get_single_rate(struct ath10k *ar, } static int ath10k_mac_set_fixed_rate_params(struct ath10k_vif *arvif, - u8 rate, u8 nss, u8 sgi) + u8 rate, u8 nss, u8 sgi, u8 ldpc) { struct ath10k *ar = arvif->ar; u32 vdev_param; @@ -5934,6 +5990,13 @@ static int ath10k_mac_set_fixed_rate_params(struct ath10k_vif *arvif, return ret; } + vdev_param = ar->wmi.vdev_param->ldpc; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ldpc); + if (ret) { + ath10k_warn(ar, "failed to set ldpc param %d: %d\n", ldpc, ret); + return ret; + } + return 0; } @@ -5997,6 +6060,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, u8 rate; u8 nss; u8 sgi; + u8 ldpc; int single_nss; int ret; @@ -6006,6 +6070,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, band = def.chan->band; ht_mcs_mask = mask->control[band].ht_mcs; vht_mcs_mask = mask->control[band].vht_mcs; + ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC); sgi = mask->control[band].gi; if (sgi == NL80211_TXRATE_FORCE_LGI) @@ -6044,7 +6109,7 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); - ret = ath10k_mac_set_fixed_rate_params(arvif, rate, nss, sgi); + ret = ath10k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc); if (ret) { ath10k_warn(ar, "failed to set fixed rate params on vdev %i: %d\n", arvif->vdev_id, ret); @@ -6144,7 +6209,7 @@ static int ath10k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); @@ -6218,6 +6283,94 @@ ath10k_mac_update_rx_channel(struct ath10k *ar, rcu_read_unlock(); } +static void +ath10k_mac_update_vif_chan(struct ath10k *ar, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs) +{ + struct ath10k_vif *arvif; + int ret; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + /* First stop monitor interface. Some FW versions crash if there's a + * lone monitor interface. + */ + if (ar->monitor_started) + ath10k_monitor_stop(ar); + + for (i = 0; i < n_vifs; i++) { + arvif = ath10k_vif_to_arvif(vifs[i].vif); + + ath10k_dbg(ar, ATH10K_DBG_MAC, + "mac chanctx switch vdev_id %i freq %hu->%hu width %d->%d\n", + arvif->vdev_id, + vifs[i].old_ctx->def.chan->center_freq, + vifs[i].new_ctx->def.chan->center_freq, + vifs[i].old_ctx->def.width, + vifs[i].new_ctx->def.width); + + if (WARN_ON(!arvif->is_started)) + continue; + + if (WARN_ON(!arvif->is_up)) + continue; + + ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) { + ath10k_warn(ar, "failed to down vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } + + /* All relevant vdevs are downed and associated channel resources + * should be available for the channel switch now. + */ + + spin_lock_bh(&ar->data_lock); + ath10k_mac_update_rx_channel(ar, NULL, vifs, n_vifs); + spin_unlock_bh(&ar->data_lock); + + for (i = 0; i < n_vifs; i++) { + arvif = ath10k_vif_to_arvif(vifs[i].vif); + + if (WARN_ON(!arvif->is_started)) + continue; + + if (WARN_ON(!arvif->is_up)) + continue; + + ret = ath10k_mac_setup_bcn_tmpl(arvif); + if (ret) + ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n", + ret); + + ret = ath10k_mac_setup_prb_tmpl(arvif); + if (ret) + ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n", + ret); + + ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def); + if (ret) { + ath10k_warn(ar, "failed to restart vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + + ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, + arvif->bssid); + if (ret) { + ath10k_warn(ar, "failed to bring vdev up %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } + + ath10k_monitor_recalc(ar); +} + static int ath10k_mac_op_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) @@ -6264,12 +6417,52 @@ ath10k_mac_op_remove_chanctx(struct ieee80211_hw *hw, mutex_unlock(&ar->conf_mutex); } +struct ath10k_mac_change_chanctx_arg { + struct ieee80211_chanctx_conf *ctx; + struct ieee80211_vif_chanctx_switch *vifs; + int n_vifs; + int next_vif; +}; + +static void +ath10k_mac_change_chanctx_cnt_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath10k_mac_change_chanctx_arg *arg = data; + + if (rcu_access_pointer(vif->chanctx_conf) != arg->ctx) + return; + + arg->n_vifs++; +} + +static void +ath10k_mac_change_chanctx_fill_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath10k_mac_change_chanctx_arg *arg = data; + struct ieee80211_chanctx_conf *ctx; + + ctx = rcu_access_pointer(vif->chanctx_conf); + if (ctx != arg->ctx) + return; + + if (WARN_ON(arg->next_vif == arg->n_vifs)) + return; + + arg->vifs[arg->next_vif].vif = vif; + arg->vifs[arg->next_vif].old_ctx = ctx; + arg->vifs[arg->next_vif].new_ctx = ctx; + arg->next_vif++; +} + static void ath10k_mac_op_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx, u32 changed) { struct ath10k *ar = hw->priv; + struct ath10k_mac_change_chanctx_arg arg = { .ctx = ctx }; mutex_lock(&ar->conf_mutex); @@ -6283,6 +6476,30 @@ ath10k_mac_op_change_chanctx(struct ieee80211_hw *hw, if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL)) goto unlock; + if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) { + ieee80211_iterate_active_interfaces_atomic( + hw, + IEEE80211_IFACE_ITER_NORMAL, + ath10k_mac_change_chanctx_cnt_iter, + &arg); + if (arg.n_vifs == 0) + goto radar; + + arg.vifs = kcalloc(arg.n_vifs, sizeof(arg.vifs[0]), + GFP_KERNEL); + if (!arg.vifs) + goto radar; + + ieee80211_iterate_active_interfaces_atomic( + hw, + IEEE80211_IFACE_ITER_NORMAL, + ath10k_mac_change_chanctx_fill_iter, + &arg); + ath10k_mac_update_vif_chan(ar, arg.vifs, arg.n_vifs); + kfree(arg.vifs); + } + +radar: ath10k_recalc_radar_detection(ar); /* FIXME: How to configure Rx chains properly? */ @@ -6402,91 +6619,13 @@ ath10k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw, enum ieee80211_chanctx_switch_mode mode) { struct ath10k *ar = hw->priv; - struct ath10k_vif *arvif; - int ret; - int i; mutex_lock(&ar->conf_mutex); ath10k_dbg(ar, ATH10K_DBG_MAC, "mac chanctx switch n_vifs %d mode %d\n", n_vifs, mode); - - /* First stop monitor interface. Some FW versions crash if there's a - * lone monitor interface. - */ - if (ar->monitor_started) - ath10k_monitor_stop(ar); - - for (i = 0; i < n_vifs; i++) { - arvif = ath10k_vif_to_arvif(vifs[i].vif); - - ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx switch vdev_id %i freq %hu->%hu width %d->%d\n", - arvif->vdev_id, - vifs[i].old_ctx->def.chan->center_freq, - vifs[i].new_ctx->def.chan->center_freq, - vifs[i].old_ctx->def.width, - vifs[i].new_ctx->def.width); - - if (WARN_ON(!arvif->is_started)) - continue; - - if (WARN_ON(!arvif->is_up)) - continue; - - ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); - if (ret) { - ath10k_warn(ar, "failed to down vdev %d: %d\n", - arvif->vdev_id, ret); - continue; - } - } - - /* All relevant vdevs are downed and associated channel resources - * should be available for the channel switch now. - */ - - spin_lock_bh(&ar->data_lock); - ath10k_mac_update_rx_channel(ar, NULL, vifs, n_vifs); - spin_unlock_bh(&ar->data_lock); - - for (i = 0; i < n_vifs; i++) { - arvif = ath10k_vif_to_arvif(vifs[i].vif); - - if (WARN_ON(!arvif->is_started)) - continue; - - if (WARN_ON(!arvif->is_up)) - continue; - - ret = ath10k_mac_setup_bcn_tmpl(arvif); - if (ret) - ath10k_warn(ar, "failed to update bcn tmpl during csa: %d\n", - ret); - - ret = ath10k_mac_setup_prb_tmpl(arvif); - if (ret) - ath10k_warn(ar, "failed to update prb tmpl during csa: %d\n", - ret); - - ret = ath10k_vdev_restart(arvif, &vifs[i].new_ctx->def); - if (ret) { - ath10k_warn(ar, "failed to restart vdev %d: %d\n", - arvif->vdev_id, ret); - continue; - } - - ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, - arvif->bssid); - if (ret) { - ath10k_warn(ar, "failed to bring vdev up %d: %d\n", - arvif->vdev_id, ret); - continue; - } - } - - ath10k_monitor_recalc(ar); + ath10k_mac_update_vif_chan(ar, vifs, n_vifs); mutex_unlock(&ar->conf_mutex); return 0; @@ -6642,6 +6781,9 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = { { .max = 7, .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif }, }; @@ -6649,6 +6791,9 @@ static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = { { .max = 8, .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif }, }; @@ -6686,6 +6831,9 @@ static const struct ieee80211_iface_limit ath10k_tlv_if_limit[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO), }, @@ -6707,6 +6855,9 @@ static const struct ieee80211_iface_limit ath10k_tlv_qcs_if_limit[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif BIT(NL80211_IFTYPE_P2P_GO), }, { @@ -6773,6 +6924,9 @@ static const struct ieee80211_iface_limit ath10k_10_4_if_limits[] = { { .max = 16, .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif }, }; @@ -6804,7 +6958,7 @@ static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) { - val = ar->num_rf_chains - 1; + val = ath10k_mac_get_vht_cap_bf_sts(ar); val <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; val &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; @@ -6813,7 +6967,7 @@ static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) { - val = ar->num_rf_chains - 1; + val = ath10k_mac_get_vht_cap_bf_sound_dim(ar); val <<= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; val &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; @@ -6997,7 +7151,8 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP); + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT); ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask; ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask; diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 1046ab65b9ab..110fcad609b9 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2609,12 +2609,9 @@ static int ath10k_pci_request_irq(struct ath10k *ar) return ath10k_pci_request_irq_legacy(ar); case 1: return ath10k_pci_request_irq_msi(ar); - case MSI_NUM_REQUEST: + default: return ath10k_pci_request_irq_msix(ar); } - - ath10k_warn(ar, "unknown irq configuration upon request\n"); - return -EINVAL; } static void ath10k_pci_free_irq(struct ath10k *ar) @@ -2657,7 +2654,7 @@ static int ath10k_pci_init_irq(struct ath10k *ar) /* Try MSI-X */ if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO) { - ar_pci->num_msi_intrs = MSI_NUM_REQUEST; + ar_pci->num_msi_intrs = MSI_ASSIGN_CE_MAX + 1; ret = pci_enable_msi_range(ar_pci->pdev, ar_pci->num_msi_intrs, ar_pci->num_msi_intrs); if (ret > 0) @@ -2705,18 +2702,13 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar) switch (ar_pci->num_msi_intrs) { case 0: ath10k_pci_deinit_irq_legacy(ar); - return 0; - case 1: - /* fall-through */ - case MSI_NUM_REQUEST: - pci_disable_msi(ar_pci->pdev); - return 0; + break; default: pci_disable_msi(ar_pci->pdev); + break; } - ath10k_warn(ar, "unknown irq configuration upon deinit\n"); - return -EINVAL; + return 0; } static int ath10k_pci_wait_for_target_init(struct ath10k *ar) diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index e4a9c4c8d0cb..7db7d501726b 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -52,6 +52,9 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct ieee80211_tx_info *info; struct ath10k_skb_cb *skb_cb; struct sk_buff *msdu; + struct ieee80211_hdr *hdr; + __le16 fc; + bool limit_mgmt_desc = false; ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d success %d\n", @@ -72,14 +75,21 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, spin_unlock_bh(&htt->tx_lock); return; } + + hdr = (struct ieee80211_hdr *)msdu->data; + fc = hdr->frame_control; + + if (unlikely(ieee80211_is_mgmt(fc)) && + ar->hw_params.max_probe_resp_desc_thres) + limit_mgmt_desc = true; + ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id); - __ath10k_htt_tx_dec_pending(htt); + __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc); if (htt->num_pending_tx == 0) wake_up(&htt->empty_tx_wq); spin_unlock_bh(&htt->tx_lock); skb_cb = ATH10K_SKB_CB(msdu); - dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); if (skb_cb->htt.txbuf) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index ce01107ef37a..87d9de2aa8c5 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3917,6 +3917,53 @@ static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, return 0; } +static bool +ath10k_wmi_is_host_mem_allocated(struct ath10k *ar, + const struct wlan_host_mem_req **mem_reqs, + u32 num_mem_reqs) +{ + u32 req_id, num_units, unit_size, num_unit_info; + u32 pool_size; + int i, j; + bool found; + + if (ar->wmi.num_mem_chunks != num_mem_reqs) + return false; + + for (i = 0; i < num_mem_reqs; ++i) { + req_id = __le32_to_cpu(mem_reqs[i]->req_id); + num_units = __le32_to_cpu(mem_reqs[i]->num_units); + unit_size = __le32_to_cpu(mem_reqs[i]->unit_size); + num_unit_info = __le32_to_cpu(mem_reqs[i]->num_unit_info); + + if (num_unit_info & NUM_UNITS_IS_NUM_ACTIVE_PEERS) { + if (ar->num_active_peers) + num_units = ar->num_active_peers + 1; + else + num_units = ar->max_num_peers + 1; + } else if (num_unit_info & NUM_UNITS_IS_NUM_PEERS) { + num_units = ar->max_num_peers + 1; + } else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS) { + num_units = ar->max_num_vdevs + 1; + } + + found = false; + for (j = 0; j < ar->wmi.num_mem_chunks; j++) { + if (ar->wmi.mem_chunks[j].req_id == req_id) { + pool_size = num_units * round_up(unit_size, 4); + if (ar->wmi.mem_chunks[j].len == pool_size) { + found = true; + break; + } + } + } + if (!found) + return false; + } + + return true; +} + static int ath10k_wmi_main_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, struct wmi_svc_rdy_ev_arg *arg) @@ -3997,6 +4044,7 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) struct wmi_svc_rdy_ev_arg arg = {}; u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i; int ret; + bool allocated; if (!skb) { ath10k_warn(ar, "invalid service ready event skb\n"); @@ -4073,6 +4121,18 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) * and WMI_SERVICE_IRAM_TIDS, etc. */ + allocated = ath10k_wmi_is_host_mem_allocated(ar, arg.mem_reqs, + num_mem_reqs); + if (allocated) + goto skip_mem_alloc; + + /* Either this event is received during boot time or there is a change + * in memory requirement from firmware when compared to last request. + * Free any old memory and do a fresh allocation based on the current + * memory requirement. + */ + ath10k_wmi_free_host_mem(ar); + for (i = 0; i < num_mem_reqs; ++i) { req_id = __le32_to_cpu(arg.mem_reqs[i]->req_id); num_units = __le32_to_cpu(arg.mem_reqs[i]->num_units); @@ -4108,6 +4168,7 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) return; } +skip_mem_alloc: ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x fw_build 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n", __le32_to_cpu(arg.min_tx_power), @@ -6660,15 +6721,10 @@ int ath10k_wmi_attach(struct ath10k *ar) return 0; } -void ath10k_wmi_detach(struct ath10k *ar) +void ath10k_wmi_free_host_mem(struct ath10k *ar) { int i; - cancel_work_sync(&ar->svc_rdy_work); - - if (ar->svc_rdy_skb) - dev_kfree_skb(ar->svc_rdy_skb); - /* free the host memory chunks requested by firmware */ for (i = 0; i < ar->wmi.num_mem_chunks; i++) { dma_free_coherent(ar->dev, @@ -6679,3 +6735,11 @@ void ath10k_wmi_detach(struct ath10k *ar) ar->wmi.num_mem_chunks = 0; } + +void ath10k_wmi_detach(struct ath10k *ar) +{ + cancel_work_sync(&ar->svc_rdy_work); + + if (ar->svc_rdy_skb) + dev_kfree_skb(ar->svc_rdy_skb); +} diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 52d35032d53e..3e5a1591f772 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -6067,6 +6067,7 @@ struct ath10k_fw_stats_peer; int ath10k_wmi_attach(struct ath10k *ar); void ath10k_wmi_detach(struct ath10k *ar); +void ath10k_wmi_free_host_mem(struct ath10k *ar); int ath10k_wmi_wait_for_service_ready(struct ath10k *ar); int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index a511ef3614b9..3fda750db2a9 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3312,7 +3312,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, } /* fw uses seconds, also make sure that it's >0 */ - interval = max_t(u16, 1, request->interval / 1000); + interval = max_t(u16, 1, request->scan_plans[0].interval); ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, interval, interval, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 174442beb952..0c391997a2f7 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -1249,7 +1249,8 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0); - if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) { + if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) || + AR_SREV_9561(ah)) { if (is_2g) REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, @@ -1640,7 +1641,8 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah, skip_tx_iqcal: if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { - if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) { + if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah) || + AR_SREV_9561(ah)) { for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->rxchainmask & (1 << i))) continue; diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index c85c47978e1e..b42f4a963ef4 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -635,6 +635,7 @@ struct ath9k_vif_iter_data { int nstations; /* number of station vifs */ int nwds; /* number of WDS vifs */ int nadhocs; /* number of adhoc vifs */ + int nocbs; /* number of OCB vifs */ struct ieee80211_vif *primary_sta; }; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index da32c8faad94..6de64cface3c 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -741,8 +741,8 @@ static int read_file_misc(struct seq_file *file, void *data) i++, (int)(ctx->assigned), iter_data.naps, iter_data.nstations, iter_data.nmeshes, iter_data.nwds); - seq_printf(file, " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", - iter_data.nadhocs, sc->cur_chan->nvifs, + seq_printf(file, " ADHOC: %i OCB: %i TOTAL: %hi BEACON-VIF: %hi\n", + iter_data.nadhocs, iter_data.nocbs, sc->cur_chan->nvifs, sc->nbcnvifs); } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index efe77db96570..8647ab77c019 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -740,7 +740,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_MESH_POINT); + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_OCB); hw->wiphy->iface_combinations = &if_comb; hw->wiphy->n_iface_combinations = 1; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 172a9ff4aaab..a680a970b7f7 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1659,7 +1659,7 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size) + u16 tid, u16 *ssn, u8 buf_size, bool amsdu) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 1dd0339de372..bdfff4641931 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1241,6 +1241,7 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) break; } /* fall through */ + case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: set |= AR_STA_ID1_STA_AP; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 57f95f2dca5b..5d532c7b813f 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -855,7 +855,8 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT) | - BIT(NL80211_IFTYPE_WDS); + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_OCB); if (ath9k_is_chanctx_enabled()) hw->wiphy->interface_modes |= diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c27143ba9ffb..d184e682e636 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -938,6 +938,9 @@ static void ath9k_vif_iter(struct ath9k_vif_iter_data *iter_data, if (avp->assoc && !iter_data->primary_sta) iter_data->primary_sta = vif; break; + case NL80211_IFTYPE_OCB: + iter_data->nocbs++; + break; case NL80211_IFTYPE_ADHOC: iter_data->nadhocs++; if (vif->bss_conf.enable_beacon) @@ -1111,6 +1114,8 @@ void ath9k_calculate_summary_state(struct ath_softc *sc, if (iter_data.nmeshes) ah->opmode = NL80211_IFTYPE_MESH_POINT; + else if (iter_data.nocbs) + ah->opmode = NL80211_IFTYPE_OCB; else if (iter_data.nwds) ah->opmode = NL80211_IFTYPE_AP; else if (iter_data.nadhocs) @@ -1760,7 +1765,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ath9k_calculate_summary_state(sc, avp->chanctx); } - if (changed & BSS_CHANGED_IBSS) { + if ((changed & BSS_CHANGED_IBSS) || + (changed & BSS_CHANGED_OCB)) { memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); common->curaid = bss_conf->aid; ath9k_hw_write_associd(sc->sc_ah); @@ -1856,7 +1862,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size) + u16 tid, u16 *ssn, u8 buf_size, bool amsdu) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index d3189daf9996..994daf6c6297 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -403,7 +403,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) (sc->cur_chan->nvifs <= 1) && !(sc->cur_chan->rxfilter & FIF_BCN_PRBRESP_PROMISC)) rfilt |= ATH9K_RX_FILTER_MYBEACON; - else + else if (sc->sc_ah->opmode != NL80211_IFTYPE_OCB) rfilt |= ATH9K_RX_FILTER_BEACON; if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) || diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 170c209f99b8..19d3d64416bf 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1415,7 +1415,7 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size) + u16 tid, u16 *ssn, u8 buf_size, bool amsdu) { struct ar9170 *ar = hw->priv; struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 924135b8e575..d66533cbc38a 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -453,7 +453,7 @@ static void carl9170_rx_phy_status(struct ar9170 *ar, /* post-process RSSI */ for (i = 0; i < 7; i++) if (phy->rssi[i] & 0x80) - phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f; + phy->rssi[i] = ((~phy->rssi[i] & 0x7f) + 1) & 0x7f; /* TODO: we could do something with phy_errors */ status->signal = ar->noise[0] + phy->rssi_combined; diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index 656ce42b339a..9d687121b2bf 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -21,12 +21,6 @@ #include "dfs_pri_detector.h" #include "ath.h" -/* - * tolerated deviation of radar time stamp in usecs on both sides - * TODO: this might need to be HW-dependent - */ -#define PRI_TOLERANCE 16 - /** * struct radar_types - contains array of patterns defined for one DFS domain * @domain: DFS regulatory domain @@ -121,7 +115,7 @@ static const struct radar_detector_specs jp_radar_ref_types[] = { JP_PATTERN(4, 0, 5, 150, 230, 1, 23, 50, false), JP_PATTERN(5, 6, 10, 200, 500, 1, 16, 50, false), JP_PATTERN(6, 11, 20, 200, 500, 1, 12, 50, false), - JP_PATTERN(7, 50, 100, 1000, 2000, 1, 20, 50, false), + JP_PATTERN(7, 50, 100, 1000, 2000, 1, 3, 50, false), JP_PATTERN(5, 0, 1, 333, 333, 1, 9, 50, false), }; diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.h b/drivers/net/wireless/ath/dfs_pattern_detector.h index 25a43d632f90..92be3530e9b5 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.h +++ b/drivers/net/wireless/ath/dfs_pattern_detector.h @@ -21,6 +21,11 @@ #include <linux/list.h> #include <linux/nl80211.h> +/* tolerated deviation of radar time stamp in usecs on both sides + * TODO: this might need to be HW-dependent + */ +#define PRI_TOLERANCE 16 + /** * struct ath_dfs_pool_stats - DFS Statistics for global pools */ diff --git a/drivers/net/wireless/ath/dfs_pri_detector.c b/drivers/net/wireless/ath/dfs_pri_detector.c index cc5c592fc4c0..05b0464c6b92 100644 --- a/drivers/net/wireless/ath/dfs_pri_detector.c +++ b/drivers/net/wireless/ath/dfs_pri_detector.c @@ -25,6 +25,9 @@ struct ath_dfs_pool_stats global_dfs_pool_stats = {}; #define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) #define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) +#define GET_PRI_TO_USE(MIN, MAX, RUNTIME) \ + (MIN + PRI_TOLERANCE == MAX - PRI_TOLERANCE ? \ + MIN + PRI_TOLERANCE : RUNTIME) /** * struct pulse_elem - elements in pulse queue @@ -243,7 +246,8 @@ static bool pseq_handler_create_sequences(struct pri_detector *pde, ps.count_falses = 0; ps.first_ts = p->ts; ps.last_ts = ts; - ps.pri = ts - p->ts; + ps.pri = GET_PRI_TO_USE(pde->rs->pri_min, + pde->rs->pri_max, ts - p->ts); ps.dur = ps.pri * (pde->rs->ppb - 1) + 2 * pde->rs->max_pri_tolerance; diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 900e72a089d8..7c169abdbafe 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -859,7 +859,7 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct wcn36xx *wcn = hw->priv; struct wcn36xx_sta *sta_priv = NULL; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index 8e0e91c4a0b1..288c84e7c56b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -272,10 +272,11 @@ brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset, } static int -brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, - struct sk_buff *pktbuf) +brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, + struct sk_buff *pktbuf, struct brcmf_if **ifp) { struct brcmf_proto_bcdc_header *h; + struct brcmf_if *tmp_if; brcmf_dbg(BCDC, "Enter\n"); @@ -289,30 +290,21 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, trace_brcmf_bcdchdr(pktbuf->data); h = (struct brcmf_proto_bcdc_header *)(pktbuf->data); - *ifidx = BCDC_GET_IF_IDX(h); - if (*ifidx >= BRCMF_MAX_IFS) { - brcmf_err("rx data ifnum out of range (%d)\n", *ifidx); + tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h)); + if (!tmp_if) { + brcmf_dbg(INFO, "no matching ifp found\n"); return -EBADE; } - /* The ifidx is the idx to map to matching netdev/ifp. When receiving - * events this is easy because it contains the bssidx which maps - * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. - * bssidx 1 is used for p2p0 and no data can be received or - * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 - */ - if (*ifidx) - (*ifidx)++; - if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) != BCDC_PROTO_VER) { brcmf_err("%s: non-BCDC packet received, flags 0x%x\n", - brcmf_ifname(drvr, *ifidx), h->flags); + brcmf_ifname(drvr, tmp_if->ifidx), h->flags); return -EBADE; } if (h->flags & BCDC_FLAG_SUM_GOOD) { brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n", - brcmf_ifname(drvr, *ifidx), h->flags); + brcmf_ifname(drvr, tmp_if->ifidx), h->flags); pktbuf->ip_summed = CHECKSUM_UNNECESSARY; } @@ -320,12 +312,14 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, skb_pull(pktbuf, BCDC_HEADER_LEN); if (do_fws) - brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf); + brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf); else skb_pull(pktbuf, h->data_offset << 2); if (pktbuf->len == 0) return -ENODATA; + + *ifp = tmp_if; return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index 0445163991b7..4e33f96b3dd1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c @@ -149,7 +149,7 @@ static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data) static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, bool trump_sco) { - struct brcmf_if *ifp = btci->cfg->pub->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0); if (trump_sco && !btci->saved_regs_part2) { /* this should reduce eSCO agressive @@ -468,7 +468,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, { struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy); struct brcmf_btcoex_info *btci = cfg->btcoex; - struct brcmf_if *ifp = cfg->pub->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0); switch (mode) { case BRCMF_BTCOEX_DISABLED: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index a293275c1b0b..891f4ed8c5e3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -236,89 +236,6 @@ static int brcmf_roamoff; module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); MODULE_PARM_DESC(roamoff, "do not use internal roaming engine"); -/* Quarter dBm units to mW - * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153 - * Table is offset so the last entry is largest mW value that fits in - * a u16. - */ - -#define QDBM_OFFSET 153 /* Offset for first entry */ -#define QDBM_TABLE_LEN 40 /* Table size */ - -/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET. - * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2 - */ -#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */ - -/* Largest mW value that will round down to the last table entry, - * QDBM_OFFSET + QDBM_TABLE_LEN-1. - * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + - * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2. - */ -#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */ - -static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = { -/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */ -/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000, -/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849, -/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119, -/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811, -/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096 -}; - -static u16 brcmf_qdbm_to_mw(u8 qdbm) -{ - uint factor = 1; - int idx = qdbm - QDBM_OFFSET; - - if (idx >= QDBM_TABLE_LEN) - /* clamp to max u16 mW value */ - return 0xFFFF; - - /* scale the qdBm index up to the range of the table 0-40 - * where an offset of 40 qdBm equals a factor of 10 mW. - */ - while (idx < 0) { - idx += 40; - factor *= 10; - } - - /* return the mW value scaled down to the correct factor of 10, - * adding in factor/2 to get proper rounding. - */ - return (nqdBm_to_mW_map[idx] + factor / 2) / factor; -} - -static u8 brcmf_mw_to_qdbm(u16 mw) -{ - u8 qdbm; - int offset; - uint mw_uint = mw; - uint boundary; - - /* handle boundary case */ - if (mw_uint <= 1) - return 0; - - offset = QDBM_OFFSET; - - /* move mw into the range of the table */ - while (mw_uint < QDBM_TABLE_LOW_BOUND) { - mw_uint *= 10; - offset -= 40; - } - - for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) { - boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] - - nqdBm_to_mW_map[qdbm]) / 2; - if (mw_uint < boundary) - break; - } - - qdbm += (u8) offset; - - return qdbm; -} static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, struct cfg80211_chan_def *ch) @@ -860,6 +777,37 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, s32 err = 0; brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type); + + /* WAR: There are a number of p2p interface related problems which + * need to be handled initially (before doing the validate). + * wpa_supplicant tends to do iface changes on p2p device/client/go + * which are not always possible/allowed. However we need to return + * OK otherwise the wpa_supplicant wont start. The situation differs + * on configuration and setup (p2pon=1 module param). The first check + * is to see if the request is a change to station for p2p iface. + */ + if ((type == NL80211_IFTYPE_STATION) && + ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) || + (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) || + (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) { + brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n"); + /* Now depending on whether module param p2pon=1 was used the + * response needs to be either 0 or EOPNOTSUPP. The reason is + * that if p2pon=1 is used, but a newer supplicant is used then + * we should return an error, as this combination wont work. + * In other situations 0 is returned and supplicant will start + * normally. It will give a trace in cfg80211, but it is the + * only way to get it working. Unfortunately this will result + * in situation where we wont support new supplicant in + * combination with module param p2pon=1, but that is the way + * it is. If the user tries this then unloading of driver might + * fail/lock. + */ + if (cfg->p2p.p2pdev_dynamically) + return -EOPNOTSUPP; + else + return 0; + } err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type); if (err) { brcmf_err("iface validation failed: err=%d\n", err); @@ -875,18 +823,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, infra = 0; break; case NL80211_IFTYPE_STATION: - /* Ignore change for p2p IF. Unclear why supplicant does this */ - if ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) || - (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) { - brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n"); - /* WAR: It is unexpected to get a change of VIF for P2P - * IF, but it happens. The request can not be handled - * but returning EPERM causes a crash. Returning 0 - * without setting ieee80211_ptr->iftype causes trace - * (WARN_ON) but it works with wpa_supplicant - */ - return 0; - } infra = 1; break; case NL80211_IFTYPE_AP: @@ -2017,16 +1953,14 @@ static s32 brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_tx_power_setting type, s32 mbm) { - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg); struct brcmf_if *ifp = netdev_priv(ndev); - u16 txpwrmw; - s32 err = 0; - s32 disable = 0; - s32 dbm = MBM_TO_DBM(mbm); + s32 err; + s32 disable; + u32 qdbm = 127; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm); if (!check_vif_up(ifp->vif)) return -EIO; @@ -2035,12 +1969,20 @@ brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, break; case NL80211_TX_POWER_LIMITED: case NL80211_TX_POWER_FIXED: - if (dbm < 0) { + if (mbm < 0) { brcmf_err("TX_POWER_FIXED - dbm is negative\n"); err = -EINVAL; goto done; } + qdbm = MBM_TO_DBM(4 * mbm); + if (qdbm > 127) + qdbm = 127; + qdbm |= WL_TXPWR_OVERRIDE; break; + default: + brcmf_err("Unsupported type %d\n", type); + err = -EINVAL; + goto done; } /* Make sure radio is off or on as far as software is concerned */ disable = WL_RADIO_SW_DISABLE << 16; @@ -2048,52 +1990,44 @@ brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, if (err) brcmf_err("WLC_SET_RADIO error (%d)\n", err); - if (dbm > 0xffff) - txpwrmw = 0xffff; - else - txpwrmw = (u16) dbm; - err = brcmf_fil_iovar_int_set(ifp, "qtxpower", - (s32)brcmf_mw_to_qdbm(txpwrmw)); + err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm); if (err) brcmf_err("qtxpower error (%d)\n", err); - cfg->conf->tx_power = dbm; done: - brcmf_dbg(TRACE, "Exit\n"); + brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE); return err; } -static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, - struct wireless_dev *wdev, - s32 *dbm) +static s32 +brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, + s32 *dbm) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - s32 txpwrdbm; - u8 result; - s32 err = 0; + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 qdbm = 0; + s32 err; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) return -EIO; - err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm); + err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm); if (err) { brcmf_err("error (%d)\n", err); goto done; } - - result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE); - *dbm = (s32) brcmf_qdbm_to_mw(result); + *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4; done: - brcmf_dbg(TRACE, "Exit\n"); + brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm); return err; } static s32 brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool unicast, bool multicast) + u8 key_idx, bool unicast, bool multicast) { struct brcmf_if *ifp = netdev_priv(ndev); u32 index; @@ -4747,7 +4681,8 @@ void brcmf_cfg80211_free_netdev(struct net_device *ndev) ifp = netdev_priv(ndev); vif = ifp->vif; - brcmf_free_vif(vif); + if (vif) + brcmf_free_vif(vif); free_netdev(ndev); } @@ -4983,7 +4918,7 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, brcmf_dbg(CONN, "AP mode link down\n"); complete(&cfg->vif_disabled); if (ifp->vif->mbss) - brcmf_remove_interface(ifp->drvr, ifp->bssidx); + brcmf_remove_interface(ifp); return 0; } @@ -6211,9 +6146,10 @@ static void brcmf_free_wiphy(struct wiphy *wiphy) } struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev) + struct device *busdev, + bool p2pdev_forced) { - struct net_device *ndev = drvr->iflist[0]->ndev; + struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev; struct brcmf_cfg80211_info *cfg; struct wiphy *wiphy; struct brcmf_cfg80211_vif *vif; @@ -6303,7 +6239,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; } - err = brcmf_p2p_attach(cfg); + err = brcmf_p2p_attach(cfg, p2pdev_forced); if (err) { brcmf_err("P2P initilisation failed (%d)\n", err); goto wiphy_unreg_out; @@ -6331,6 +6267,7 @@ wiphy_unreg_out: priv_out: wl_deinit_priv(cfg); brcmf_free_vif(vif); + ifp->vif = NULL; wiphy_out: brcmf_free_wiphy(wiphy); return NULL; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h index d9e6d01b2b69..3f5e5505d329 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h @@ -469,7 +469,8 @@ brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg) } struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev); + struct device *busdev, + bool p2pdev_forced); void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg); s32 brcmf_cfg80211_up(struct net_device *ndev); s32 brcmf_cfg80211_down(struct net_device *ndev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 288f8314f208..ffc3ace24903 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -101,6 +101,9 @@ /* ARM Cortex M3 core, ID 0x82a */ #define BCM4329_CORE_ARM_BASE 0x18002000 +/* Max possibly supported memory size (limited by IO mapped memory) */ +#define BRCMF_CHIP_MAX_MEMSIZE (4 * 1024 * 1024) + #define CORE_SB(base, field) \ (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) #define SBCOREREV(sbidh) \ @@ -205,6 +208,7 @@ struct sbsocramregs { }; #define SOCRAMREGOFFS(_f) offsetof(struct sbsocramregs, _f) +#define SYSMEMREGOFFS(_f) offsetof(struct sbsocramregs, _f) #define ARMCR4_CAP (0x04) #define ARMCR4_BANKIDX (0x40) @@ -513,6 +517,9 @@ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) case BCMA_CORE_ARM_CR4: cpu_found = true; break; + case BCMA_CORE_ARM_CA7: + cpu_found = true; + break; default: break; } @@ -611,6 +618,29 @@ static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, } } +/** Return the SYS MEM size */ +static u32 brcmf_chip_sysmem_ramsize(struct brcmf_core_priv *sysmem) +{ + u32 memsize = 0; + u32 coreinfo; + u32 idx; + u32 nb; + u32 banksize; + + if (!brcmf_chip_iscoreup(&sysmem->pub)) + brcmf_chip_resetcore(&sysmem->pub, 0, 0, 0); + + coreinfo = brcmf_chip_core_read32(sysmem, SYSMEMREGOFFS(coreinfo)); + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + + for (idx = 0; idx < nb; idx++) { + brcmf_chip_socram_banksize(sysmem, idx, &banksize); + memsize += banksize; + } + + return memsize; +} + /** Return the TCM-RAM size of the ARMCR4 core. */ static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4) { @@ -644,6 +674,7 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) return 0x198000; case BRCM_CC_4335_CHIP_ID: case BRCM_CC_4339_CHIP_ID: + case BRCM_CC_4350_CHIP_ID: case BRCM_CC_4354_CHIP_ID: case BRCM_CC_4356_CHIP_ID: case BRCM_CC_43567_CHIP_ID: @@ -652,6 +683,9 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) case BRCM_CC_4358_CHIP_ID: case BRCM_CC_43602_CHIP_ID: return 0x180000; + case BRCM_CC_4365_CHIP_ID: + case BRCM_CC_4366_CHIP_ID: + return 0x200000; default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; @@ -674,10 +708,28 @@ static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) 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); + mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_SYS_MEM); + if (mem) { + mem_core = container_of(mem, struct brcmf_core_priv, + pub); + ci->pub.ramsize = brcmf_chip_sysmem_ramsize(mem_core); + ci->pub.rambase = brcmf_chip_tcm_rambase(ci); + if (!ci->pub.rambase) { + brcmf_err("RAM base not provided with ARM CA7 core\n"); + return -EINVAL; + } + } else { + mem = brcmf_chip_get_core(&ci->pub, + BCMA_CORE_INTERNAL_MEM); + if (!mem) { + brcmf_err("No memory cores found\n"); + return -ENOMEM; + } + 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, @@ -687,6 +739,12 @@ static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) brcmf_err("RAM size is undetermined\n"); return -ENOMEM; } + + if (ci->pub.ramsize > BRCMF_CHIP_MAX_MEMSIZE) { + brcmf_err("RAM size is incorrect\n"); + return -ENOMEM; + } + return 0; } @@ -899,13 +957,22 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) /* assure chip is passive for core access */ brcmf_chip_set_passive(&ci->pub); + + /* Call bus specific reset function now. Cores have been determined + * but further access may require a chip specific reset at this point. + */ + if (ci->ops->reset) { + ci->ops->reset(ci->ctx, &ci->pub); + 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) { struct brcmf_core *core; - struct brcmf_core_priv *cr4; + struct brcmf_core_priv *cpu; u32 val; @@ -918,10 +985,11 @@ static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) brcmf_chip_coredisable(core, 0, 0); break; case BCMA_CORE_ARM_CR4: - cr4 = container_of(core, struct brcmf_core_priv, pub); + case BCMA_CORE_ARM_CA7: + cpu = container_of(core, struct brcmf_core_priv, pub); /* clear all IOCTL bits except HALT bit */ - val = chip->ops->read32(chip->ctx, cr4->wrapbase + BCMA_IOCTL); + val = chip->ops->read32(chip->ctx, cpu->wrapbase + BCMA_IOCTL); val &= ARMCR4_BCMA_IOCTL_CPUHALT; brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT, ARMCR4_BCMA_IOCTL_CPUHALT); @@ -1143,6 +1211,33 @@ static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec) return true; } +static inline void +brcmf_chip_ca7_set_passive(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CA7); + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); +} + +static bool brcmf_chip_ca7_set_active(struct brcmf_chip_priv *chip, u32 rstvec) +{ + struct brcmf_core *core; + + chip->ops->activate(chip->ctx, &chip->pub, rstvec); + + /* restore ARM */ + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CA7); + brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0); + + return true; +} + void brcmf_chip_set_passive(struct brcmf_chip *pub) { struct brcmf_chip_priv *chip; @@ -1156,8 +1251,16 @@ void brcmf_chip_set_passive(struct brcmf_chip *pub) brcmf_chip_cr4_set_passive(chip); return; } - - brcmf_chip_cm3_set_passive(chip); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7); + if (arm) { + brcmf_chip_ca7_set_passive(chip); + return; + } + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + if (arm) { + brcmf_chip_cm3_set_passive(chip); + return; + } } bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec) @@ -1171,8 +1274,14 @@ bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec) arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); if (arm) return brcmf_chip_cr4_set_active(chip, rstvec); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7); + if (arm) + return brcmf_chip_ca7_set_active(chip, rstvec); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + if (arm) + return brcmf_chip_cm3_set_active(chip); - return brcmf_chip_cm3_set_active(chip); + return false; } bool brcmf_chip_sr_capable(struct brcmf_chip *pub) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h index 60dcb38fc77a..f6b5feea23d2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h @@ -73,6 +73,7 @@ struct brcmf_buscore_ops { u32 (*read32)(void *ctx, u32 addr); void (*write32)(void *ctx, u32 addr, u32 value); int (*prepare)(void *ctx); + int (*reset)(void *ctx, struct brcmf_chip *chip); int (*setup)(void *ctx, struct brcmf_chip *chip); void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec); }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index fe9d3fbf5fe2..8c2a280f0c98 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -53,6 +53,8 @@ MODULE_LICENSE("Dual BSD/GPL"); #define BRCMF_RXREORDER_EXPIDX_VALID 0x08 #define BRCMF_RXREORDER_NEW_HOLE 0x10 +#define BRCMF_BSSIDX_INVALID -1 + /* Error bits */ int brcmf_msg_level; module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); @@ -60,10 +62,8 @@ MODULE_PARM_DESC(debug, "level of debug output"); /* P2P0 enable */ static int brcmf_p2p_enable; -#ifdef CONFIG_BRCMDBG module_param_named(p2pon, brcmf_p2p_enable, int, 0); -MODULE_PARM_DESC(p2pon, "enable p2p management functionality"); -#endif +MODULE_PARM_DESC(p2pon, "enable legacy p2p management functionality"); char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) { @@ -83,6 +83,24 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) return "<if_none>"; } +struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx) +{ + struct brcmf_if *ifp; + s32 bssidx; + + if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { + brcmf_err("ifidx %d out of range\n", ifidx); + return NULL; + } + + ifp = NULL; + bssidx = drvr->if2bss[ifidx]; + if (bssidx >= 0) + ifp = drvr->iflist[bssidx]; + + return ifp; +} + static void _brcmf_set_multicast_list(struct work_struct *work) { struct brcmf_if *ifp; @@ -520,17 +538,15 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; struct brcmf_skb_reorder_data *rd; - u8 ifidx; int ret; brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb); /* process and remove protocol-specific header */ - ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb); - ifp = drvr->iflist[ifidx]; + ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp); if (ret || !ifp || !ifp->ndev) { - if ((ret != -ENODATA) && ifp) + if (ret != -ENODATA && ifp) ifp->stats.rx_errors++; brcmu_pkt_buf_free_skb(skb); return; @@ -543,17 +559,11 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) brcmf_netif_rx(ifp, skb); } -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, - bool success) +void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success) { - struct brcmf_if *ifp; struct ethhdr *eh; u16 type; - ifp = drvr->iflist[ifidx]; - if (!ifp) - goto done; - eh = (struct ethhdr *)(txp->data); type = ntohs(eh->h_proto); @@ -565,7 +575,7 @@ void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, if (!success) ifp->stats.tx_errors++; -done: + brcmu_pkt_buf_free_skb(txp); } @@ -573,17 +583,17 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; - u8 ifidx; + struct brcmf_if *ifp; /* await txstatus signal for firmware if active */ if (brcmf_fws_fc_active(drvr->fws)) { if (!success) brcmf_fws_bustxfail(drvr->fws, txp); } else { - if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp)) + if (brcmf_proto_hdrpull(drvr, false, txp, &ifp)) brcmu_pkt_buf_free_skb(txp); else - brcmf_txfinalize(drvr, txp, ifidx, success); + brcmf_txfinalize(ifp, txp, success); } } @@ -708,8 +718,6 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) } brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); - - ndev->destructor = brcmf_cfg80211_free_netdev; return 0; fail: @@ -719,6 +727,14 @@ fail: return -EBADE; } +static void brcmf_net_detach(struct net_device *ndev) +{ + if (ndev->reg_state == NETREG_REGISTERED) + unregister_netdev(ndev); + else + brcmf_cfg80211_free_netdev(ndev); +} + static int brcmf_net_p2p_open(struct net_device *ndev) { brcmf_dbg(TRACE, "Enter\n"); @@ -778,7 +794,7 @@ fail: } struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, - char *name, u8 *mac_addr) + bool is_p2pdev, char *name, u8 *mac_addr) { struct brcmf_if *ifp; struct net_device *ndev; @@ -795,8 +811,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, ifp->ndev->name); if (ifidx) { netif_stop_queue(ifp->ndev); - unregister_netdev(ifp->ndev); - free_netdev(ifp->ndev); + brcmf_net_detach(ifp->ndev); drvr->iflist[bssidx] = NULL; } else { brcmf_err("ignore IF event\n"); @@ -804,7 +819,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, } } - if (!brcmf_p2p_enable && bssidx == 1) { + if (!brcmf_p2p_enable && is_p2pdev) { /* this is P2P_DEVICE interface */ brcmf_dbg(INFO, "allocate non-netdev interface\n"); ifp = kzalloc(sizeof(*ifp), GFP_KERNEL); @@ -818,8 +833,12 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, if (!ndev) return ERR_PTR(-ENOMEM); + ndev->destructor = brcmf_cfg80211_free_netdev; ifp = netdev_priv(ndev); ifp->ndev = ndev; + /* store mapping ifidx to bssidx */ + if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID) + drvr->if2bss[ifidx] = bssidx; } ifp->drvr = drvr; @@ -850,6 +869,8 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) return; } brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx); + if (drvr->if2bss[ifp->ifidx] == bssidx) + drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID; if (ifp->ndev) { if (bssidx == 0) { if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { @@ -865,17 +886,28 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) cancel_work_sync(&ifp->setmacaddr_work); cancel_work_sync(&ifp->multicast_work); } - /* unregister will take care of freeing it */ - unregister_netdev(ifp->ndev); + brcmf_net_detach(ifp->ndev); + } else { + /* Only p2p device interfaces which get dynamically created + * end up here. In this case the p2p module should be informed + * about the removal of the interface within the firmware. If + * not then p2p commands towards the firmware will cause some + * serious troublesome side effects. The p2p module will clean + * up the ifp if needed. + */ + brcmf_p2p_ifp_removed(ifp); + kfree(ifp); } } -void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx) +void brcmf_remove_interface(struct brcmf_if *ifp) { - if (drvr->iflist[bssidx]) { - brcmf_fws_del_interface(drvr->iflist[bssidx]); - brcmf_del_if(drvr, bssidx); - } + if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp)) + return; + brcmf_dbg(TRACE, "Enter, bssidx=%d, ifidx=%d\n", ifp->bssidx, + ifp->ifidx); + brcmf_fws_del_interface(ifp); + brcmf_del_if(ifp->drvr, ifp->bssidx); } int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr) @@ -906,6 +938,7 @@ int brcmf_attach(struct device *dev) { struct brcmf_pub *drvr = NULL; int ret = 0; + int i; brcmf_dbg(TRACE, "Enter\n"); @@ -914,6 +947,9 @@ int brcmf_attach(struct device *dev) if (!drvr) return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++) + drvr->if2bss[i] = BRCMF_BSSIDX_INVALID; + mutex_init(&drvr->proto_block); /* Link to bus module */ @@ -981,12 +1017,12 @@ int brcmf_bus_start(struct device *dev) brcmf_dbg(TRACE, "\n"); /* add primary networking interface */ - ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL); + ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL); if (IS_ERR(ifp)) return PTR_ERR(ifp); if (brcmf_p2p_enable) - p2p_ifp = brcmf_add_if(drvr, 1, 0, "p2p%d", NULL); + p2p_ifp = brcmf_add_if(drvr, 1, 0, false, "p2p%d", NULL); else p2p_ifp = NULL; if (IS_ERR(p2p_ifp)) @@ -1017,7 +1053,8 @@ int brcmf_bus_start(struct device *dev) brcmf_fws_add_interface(ifp); - drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev); + drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev, + brcmf_p2p_enable); if (drvr->config == NULL) { ret = -ENOMEM; goto fail; @@ -1031,17 +1068,20 @@ int brcmf_bus_start(struct device *dev) fail: if (ret < 0) { brcmf_err("failed: %d\n", ret); - brcmf_cfg80211_detach(drvr->config); + if (drvr->config) { + brcmf_cfg80211_detach(drvr->config); + drvr->config = NULL; + } if (drvr->fws) { brcmf_fws_del_interface(ifp); brcmf_fws_deinit(drvr); } if (drvr->iflist[0]) { - free_netdev(ifp->ndev); + brcmf_net_detach(ifp->ndev); drvr->iflist[0] = NULL; } if (p2p_ifp) { - free_netdev(p2p_ifp->ndev); + brcmf_net_detach(p2p_ifp->ndev); drvr->iflist[1] = NULL; } return ret; @@ -1105,7 +1145,7 @@ void brcmf_detach(struct device *dev) /* make sure primary interface removed last */ for (i = BRCMF_MAX_IFS-1; i > -1; i--) - brcmf_remove_interface(drvr, i); + brcmf_remove_interface(drvr->iflist[i]); brcmf_cfg80211_detach(drvr->config); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 746304121cdb..d81ff95acab5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -122,6 +122,7 @@ struct brcmf_pub { struct mac_address addresses[BRCMF_MAX_IFS]; struct brcmf_if *iflist[BRCMF_MAX_IFS]; + s32 if2bss[BRCMF_MAX_IFS]; struct mutex proto_block; unsigned char proto_buf[BRCMF_DCMD_MAXLEN]; @@ -202,16 +203,15 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); /* Return pointer to interface name */ char *brcmf_ifname(struct brcmf_pub *drvr, int idx); - +struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx); int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, - char *name, u8 *mac_addr); -void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx); + bool is_p2pdev, char *name, u8 *mac_addr); +void brcmf_remove_interface(struct brcmf_if *ifp); int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, - bool success); +void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success); void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb); /* Sets dongle media info (drv_version, mac address). */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/brcm80211/brcmfmac/debug.h index eb0b8c47479d..48648ca44ba8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/debug.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.h @@ -37,6 +37,7 @@ #define BRCMF_SDIO_VAL 0x00020000 #define BRCMF_MSGBUF_VAL 0x00040000 #define BRCMF_PCIE_VAL 0x00080000 +#define BRCMF_FWCON_VAL 0x00100000 /* set default print format */ #undef pr_fmt @@ -78,6 +79,7 @@ do { \ #define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL) #define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL) #define BRCMF_FIL_ON() (brcmf_msg_level & BRCMF_FIL_VAL) +#define BRCMF_FWCON_ON() (brcmf_msg_level & BRCMF_FWCON_VAL) #else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ @@ -90,6 +92,7 @@ do { \ #define BRCMF_GLOM_ON() 0 #define BRCMF_EVENT_ON() 0 #define BRCMF_FIL_ON() 0 +#define BRCMF_FWCON_ON() 0 #endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 1e94e94e01dc..44bb30636690 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -15,6 +15,7 @@ */ #include <linux/netdevice.h> +#include <linux/module.h> #include <brcm_hw_ids.h> #include "core.h" @@ -23,6 +24,12 @@ #include "fwil.h" #include "feature.h" + +/* Module param feature_disable (global for all devices) */ +static int brcmf_feature_disable; +module_param_named(feature_disable, brcmf_feature_disable, int, 0); +MODULE_PARM_DESC(feature_disable, "Disable features"); + /* * expand feature list to array of feature strings. */ @@ -121,7 +128,7 @@ static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp, void brcmf_feat_attach(struct brcmf_pub *drvr) { - struct brcmf_if *ifp = drvr->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); @@ -131,6 +138,12 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p"); + if (brcmf_feature_disable) { + brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", + ifp->drvr->feat_flags, brcmf_feature_disable); + ifp->drvr->feat_flags &= ~brcmf_feature_disable; + } + /* set chip related quirks */ switch (drvr->bus_if->chip) { case BRCM_CC_43236_CHIP_ID: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 8d1ab4ab5be8..2ca783fa50cf 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -221,7 +221,7 @@ static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid, bus_if = dev_get_drvdata(flow->dev); drvr = bus_if->drvr; - ifp = drvr->iflist[ifidx]; + ifp = brcmf_get_ifp(drvr, ifidx); brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked); spin_unlock_irqrestore(&flow->block_lock, flags); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h index 5551861a44bc..95fd1c9675d1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h @@ -34,7 +34,7 @@ enum ring_status { }; struct brcmf_flowring_ring { - u8 hash_id; + u16 hash_id; bool blocked; enum ring_status status; struct sk_buff_head skblist; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index ec62492ffa69..383d6faf426b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -179,25 +179,28 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, { struct brcmf_if_event *ifevent = data; struct brcmf_if *ifp; + bool is_p2pdev; int err = 0; brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n", ifevent->action, ifevent->ifidx, ifevent->bssidx, ifevent->flags, ifevent->role); - /* The P2P Device interface event must not be ignored - * contrary to what firmware tells us. The only way to - * distinguish the P2P Device is by looking at the ifidx - * and bssidx received. + /* The P2P Device interface event must not be ignored contrary to what + * firmware tells us. Older firmware uses p2p noif, with sta role. + * This should be accepted when p2pdev_setup is ongoing. TDLS setup will + * use the same ifevent and should be ignored. */ - if (!(ifevent->ifidx == 0 && ifevent->bssidx == 1) && - (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) { + is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) && + (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT || + ((ifevent->role == BRCMF_E_IF_ROLE_STA) && + (drvr->fweh.p2pdev_setup_ongoing)))); + if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) { brcmf_dbg(EVENT, "event can be ignored\n"); return; } if (ifevent->ifidx >= BRCMF_MAX_IFS) { - brcmf_err("invalid interface index: %u\n", - ifevent->ifidx); + brcmf_err("invalid interface index: %u\n", ifevent->ifidx); return; } @@ -207,7 +210,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname, emsg->addr); ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx, - emsg->ifname, emsg->addr); + is_p2pdev, emsg->ifname, emsg->addr); if (IS_ERR(ifp)) return; brcmf_fws_add_interface(ifp); @@ -222,7 +225,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data); if (ifp && ifevent->action == BRCMF_E_IF_DEL) - brcmf_remove_interface(drvr, ifevent->bssidx); + brcmf_remove_interface(ifp); } /** @@ -297,8 +300,7 @@ static void brcmf_fweh_event_worker(struct work_struct *work) goto event_free; } - if ((event->code == BRCMF_E_TDLS_PEER_EVENT) && - (emsg.bsscfgidx == 1)) + if (event->code == BRCMF_E_TDLS_PEER_EVENT) ifp = drvr->iflist[0]; else ifp = drvr->iflist[emsg.bsscfgidx]; @@ -315,6 +317,17 @@ event_free: } /** + * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not). + * + * @ifp: ifp on which setup is taking place or finished. + * @ongoing: p2p device setup in progress (or not). + */ +void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing) +{ + ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing; +} + +/** * brcmf_fweh_attach() - initialize firmware event handling. * * @drvr: driver information object. @@ -335,7 +348,7 @@ void brcmf_fweh_attach(struct brcmf_pub *drvr) void brcmf_fweh_detach(struct brcmf_pub *drvr) { struct brcmf_fweh_info *fweh = &drvr->fweh; - struct brcmf_if *ifp = drvr->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); s8 eventmask[BRCMF_EVENTING_MASK_LEN]; if (ifp) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h index 1326898d608e..d9a942842382 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -230,12 +230,14 @@ typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp, /** * struct brcmf_fweh_info - firmware event handling information. * + * @p2pdev_setup_ongoing: P2P device creation in progress. * @event_work: event worker. * @evt_q_lock: lock for event queue protection. * @event_q: event queue. * @evt_handler: registered event handlers. */ struct brcmf_fweh_info { + bool p2pdev_setup_ongoing; struct work_struct event_work; spinlock_t evt_q_lock; struct list_head event_q; @@ -255,6 +257,7 @@ void brcmf_fweh_unregister(struct brcmf_pub *drvr, int brcmf_fweh_activate_events(struct brcmf_if *ifp); void brcmf_fweh_process_event(struct brcmf_pub *drvr, struct brcmf_event *event_packet); +void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing); static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, struct sk_buff *skb) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 5017eaa4af45..086cac3f86d6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -972,7 +972,7 @@ static void brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq, u8 if_id) { - struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1]; + struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id); if (WARN_ON(!ifp)) return; @@ -1398,7 +1398,7 @@ done: } static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *skb, u8 ifidx, + struct sk_buff *skb, u32 genbit, u16 seq) { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; @@ -1448,7 +1448,7 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, struct sk_buff *skb; struct brcmf_skbuff_cb *skcb; struct brcmf_fws_mac_descriptor *entry = NULL; - u8 ifidx; + struct brcmf_if *ifp; brcmf_dbg(DATA, "flags %d\n", flags); @@ -1497,15 +1497,16 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, } brcmf_fws_macdesc_return_req_credit(skb); - if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) { + ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp); + if (ret) { brcmu_pkt_buf_free_skb(skb); return -EINVAL; } if (!remove_from_hanger) - ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx, + ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit, seq); if (remove_from_hanger || ret) - brcmf_txfinalize(fws->drvr, skb, ifidx, true); + brcmf_txfinalize(ifp, skb, true); return 0; } @@ -1615,11 +1616,10 @@ static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp, return 0; } -int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, - struct sk_buff *skb) +void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb) { struct brcmf_skb_reorder_data *rd; - struct brcmf_fws_info *fws = drvr->fws; + struct brcmf_fws_info *fws = ifp->drvr->fws; u8 *signal_data; s16 data_len; u8 type; @@ -1629,20 +1629,20 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, s32 err; brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n", - ifidx, skb->len, signal_len); + ifp->ifidx, skb->len, siglen); - WARN_ON(signal_len > skb->len); + WARN_ON(siglen > skb->len); - if (!signal_len) - return 0; + if (!siglen) + return; /* if flow control disabled, skip to packet data and leave */ if ((!fws) || (!fws->fw_signals)) { - skb_pull(skb, signal_len); - return 0; + skb_pull(skb, siglen); + return; } fws->stats.header_pulls++; - data_len = signal_len; + data_len = siglen; signal_data = skb->data; status = BRCMF_FWS_RET_OK_NOSCHEDULE; @@ -1730,14 +1730,12 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, /* signalling processing result does * not affect the actual ethernet packet. */ - skb_pull(skb, signal_len); + skb_pull(skb, siglen); /* this may be a signal-only packet */ if (skb->len == 0) fws->stats.header_only_pkt++; - - return 0; } static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, @@ -1848,7 +1846,7 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, entry->transit_count--; if (entry->suppressed) entry->suppr_transit_count--; - brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); + (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL); goto rollback; } @@ -1904,7 +1902,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) if (fws->avoid_queueing) { rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb); if (rc < 0) - brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + brcmf_txfinalize(ifp, skb, false); return rc; } @@ -1928,7 +1926,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) brcmf_fws_schedule_deq(fws); } else { brcmf_err("drop skb: no hanger slot\n"); - brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + brcmf_txfinalize(ifp, skb, false); rc = -ENOMEM; } brcmf_fws_unlock(fws); @@ -2008,8 +2006,9 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) ret = brcmf_proto_txdata(drvr, ifidx, 0, skb); brcmf_fws_lock(fws); if (ret < 0) - brcmf_txfinalize(drvr, skb, ifidx, - false); + brcmf_txfinalize(brcmf_get_ifp(drvr, + ifidx), + skb, false); if (fws->bus_flow_blocked) break; } @@ -2117,6 +2116,7 @@ static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) int brcmf_fws_init(struct brcmf_pub *drvr) { struct brcmf_fws_info *fws; + struct brcmf_if *ifp; u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS; int rc; u32 mode; @@ -2176,21 +2176,22 @@ int brcmf_fws_init(struct brcmf_pub *drvr) * continue. Set mode back to none indicating not enabled. */ fws->fw_signals = true; - if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) { + ifp = brcmf_get_ifp(drvr, 0); + if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) { brcmf_err("failed to set bdcv2 tlv signaling\n"); fws->fcmode = BRCMF_FWS_FCMODE_NONE; fws->fw_signals = false; } - if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1)) + if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1)) brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n"); /* Enable seq number reuse, if supported */ - if (brcmf_fil_iovar_int_get(drvr->iflist[0], "wlfc_mode", &mode) == 0) { + if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) { if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) { mode = 0; BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1); - if (brcmf_fil_iovar_int_set(drvr->iflist[0], + if (brcmf_fil_iovar_int_set(ifp, "wlfc_mode", mode) == 0) { BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h index 9fc860910bd8..a36bac17eafd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h @@ -21,8 +21,7 @@ int brcmf_fws_init(struct brcmf_pub *drvr); void brcmf_fws_deinit(struct brcmf_pub *drvr); bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); -int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, - struct sk_buff *skb); +void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb); int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb); void brcmf_fws_reset_interface(struct brcmf_if *ifp); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 7b2136c9badb..7eff9de6885b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -522,7 +522,7 @@ static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx, static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws, - u8 *ifidx, struct sk_buff *skb) + struct sk_buff *skb, struct brcmf_if **ifp) { return -ENODEV; } @@ -873,7 +873,11 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) commonring = msgbuf->flowrings[flowid]; atomic_dec(&commonring->outstanding_tx); - brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true); + /* Hante: i believe this was a bug as tx_status->msg.ifidx was used + * in brcmf_txfinalize as index in drvr->iflist. Can you confirm/deny? + */ + brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx), + skb, true); } @@ -1081,15 +1085,7 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb, { struct brcmf_if *ifp; - /* The ifidx is the idx to map to matching netdev/ifp. When receiving - * events this is easy because it contains the bssidx which maps - * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. - * bssidx 1 is used for p2p0 and no data can be received or - * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 - */ - if (ifidx) - (ifidx)++; - ifp = msgbuf->drvr->iflist[ifidx]; + ifp = brcmf_get_ifp(msgbuf->drvr, ifidx); if (!ifp || !ifp->ndev) { brcmf_err("Received pkt for invalid ifidx %d\n", ifidx); brcmu_pkt_buf_free_skb(skb); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index a9ba775a24c1..37a8c352e077 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -2084,11 +2084,13 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr); brcmf_cfg80211_arm_vif_event(p2p->cfg, p2p_vif); + brcmf_fweh_p2pdev_setup(pri_ifp, true); /* Initialize P2P Discovery in the firmware */ err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1); if (err < 0) { brcmf_err("set p2p_disc error\n"); + brcmf_fweh_p2pdev_setup(pri_ifp, false); brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); goto fail; } @@ -2097,6 +2099,7 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD, msecs_to_jiffies(1500)); brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); + brcmf_fweh_p2pdev_setup(pri_ifp, false); if (!err) { brcmf_err("timeout occurred\n"); err = -EIO; @@ -2131,20 +2134,6 @@ fail: } /** - * brcmf_p2p_delete_p2pdev() - delete P2P_DEVICE virtual interface. - * - * @vif: virtual interface object to delete. - */ -static void brcmf_p2p_delete_p2pdev(struct brcmf_p2p_info *p2p, - struct brcmf_cfg80211_vif *vif) -{ - cfg80211_unregister_wdev(&vif->wdev); - p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; - brcmf_remove_interface(vif->ifp->drvr, vif->ifp->bssidx); - brcmf_free_vif(vif); -} - -/** * brcmf_p2p_add_vif() - create a new P2P virtual interface. * * @wiphy: wiphy device of new interface. @@ -2255,6 +2244,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) brcmf_dbg(TRACE, "delete P2P vif\n"); vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + brcmf_cfg80211_arm_vif_event(cfg, vif); switch (vif->wdev.iftype) { case NL80211_IFTYPE_P2P_CLIENT: if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state)) @@ -2267,10 +2257,10 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) break; case NL80211_IFTYPE_P2P_DEVICE: + if (!p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif) + return 0; brcmf_p2p_cancel_remain_on_channel(vif->ifp); brcmf_p2p_deinit_discovery(p2p); - brcmf_p2p_delete_p2pdev(p2p, vif); - return 0; default: return -ENOTSUPP; } @@ -2282,10 +2272,11 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) wait_for_completion_timeout(&cfg->vif_disabled, msecs_to_jiffies(500)); - brcmf_vif_clear_mgmt_ies(vif); - - brcmf_cfg80211_arm_vif_event(cfg, vif); - err = brcmf_p2p_release_p2p_if(vif); + err = 0; + if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) { + brcmf_vif_clear_mgmt_ies(vif); + err = brcmf_p2p_release_p2p_if(vif); + } if (!err) { /* wait for firmware event */ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL, @@ -2295,12 +2286,31 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) else err = 0; } + if (err) + brcmf_remove_interface(vif->ifp); + brcmf_cfg80211_arm_vif_event(cfg, NULL); - p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; return err; } +void brcmf_p2p_ifp_removed(struct brcmf_if *ifp) +{ + struct brcmf_cfg80211_info *cfg; + struct brcmf_cfg80211_vif *vif; + + brcmf_dbg(INFO, "P2P: device interface removed\n"); + vif = ifp->vif; + cfg = wdev_to_cfg(&vif->wdev); + cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; + rtnl_lock(); + cfg80211_unregister_wdev(&vif->wdev); + rtnl_unlock(); + brcmf_free_vif(vif); +} + int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); @@ -2324,11 +2334,19 @@ void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev) struct brcmf_cfg80211_vif *vif; vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - mutex_lock(&cfg->usr_sync); - (void)brcmf_p2p_deinit_discovery(p2p); - brcmf_abort_scanning(cfg); - clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); - mutex_unlock(&cfg->usr_sync); + /* This call can be result of the unregister_wdev call. In that case + * we dont want to do anything anymore. Just return. The config vif + * will have been cleared at this point. + */ + if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) { + mutex_lock(&cfg->usr_sync); + /* Set the discovery state to SCAN */ + (void)brcmf_p2p_set_discover_state(vif->ifp, + WL_P2P_DISC_ST_SCAN, 0, 0); + brcmf_abort_scanning(cfg); + clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); + mutex_unlock(&cfg->usr_sync); + } } /** @@ -2336,7 +2354,7 @@ void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev) * * @cfg: driver private data for cfg80211 interface. */ -s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg) +s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced) { struct brcmf_if *pri_ifp; struct brcmf_if *p2p_ifp; @@ -2351,11 +2369,15 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg) drvr = cfg->pub; - pri_ifp = drvr->iflist[0]; - p2p_ifp = drvr->iflist[1]; - + pri_ifp = brcmf_get_ifp(drvr, 0); p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif; + if (p2pdev_forced) { + p2p_ifp = drvr->iflist[1]; + } else { + p2p_ifp = NULL; + p2p->p2pdev_dynamically = true; + } if (p2p_ifp) { p2p_vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_P2P_DEVICE, false); @@ -2377,6 +2399,8 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg) memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN); brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr); + brcmf_fweh_p2pdev_setup(pri_ifp, true); + /* Initialize P2P Discovery in the firmware */ err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1); if (err < 0) { @@ -2403,8 +2427,9 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg) INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler); init_completion(&p2p->afx_hdl.act_frm_scan); init_completion(&p2p->wait_next_af); - } exit: + brcmf_fweh_p2pdev_setup(pri_ifp, false); + } return err; } @@ -2421,10 +2446,7 @@ void brcmf_p2p_detach(struct brcmf_p2p_info *p2p) if (vif != NULL) { brcmf_p2p_cancel_remain_on_channel(vif->ifp); brcmf_p2p_deinit_discovery(p2p); - /* remove discovery interface */ - rtnl_lock(); - brcmf_p2p_delete_p2pdev(p2p, vif); - rtnl_unlock(); + brcmf_remove_interface(vif->ifp); } /* just set it all to zero */ memset(p2p, 0, sizeof(*p2p)); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h index 872f382d9e49..5d49059021a9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h @@ -124,6 +124,7 @@ struct afx_hdl { * @wait_next_af: thread synchronizing struct. * @gon_req_action: about to send go negotiation requets frame. * @block_gon_req_tx: drop tx go negotiation requets frame. + * @p2pdev_dynamically: is p2p device if created by module param or supplicant. */ struct brcmf_p2p_info { struct brcmf_cfg80211_info *cfg; @@ -144,9 +145,10 @@ struct brcmf_p2p_info { struct completion wait_next_af; bool gon_req_action; bool block_gon_req_tx; + bool p2pdev_dynamically; }; -s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg); +s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced); void brcmf_p2p_detach(struct brcmf_p2p_info *p2p); struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, @@ -155,6 +157,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev); int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, enum brcmf_fil_p2p_if_types if_type); +void brcmf_p2p_ifp_removed(struct brcmf_if *ifp); int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev); void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev); int brcmf_p2p_scan_prep(struct wiphy *wiphy, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 3a98c4306d1d..30baf352e234 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -47,12 +47,18 @@ 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_4350_FW_NAME "brcm/brcmfmac4350-pcie.bin" +#define BRCMF_PCIE_4350_NVRAM_NAME "brcm/brcmfmac4350-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" #define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt" #define BRCMF_PCIE_4358_FW_NAME "brcm/brcmfmac4358-pcie.bin" #define BRCMF_PCIE_4358_NVRAM_NAME "brcm/brcmfmac4358-pcie.txt" +#define BRCMF_PCIE_4365_FW_NAME "brcm/brcmfmac4365b-pcie.bin" +#define BRCMF_PCIE_4365_NVRAM_NAME "brcm/brcmfmac4365b-pcie.txt" +#define BRCMF_PCIE_4366_FW_NAME "brcm/brcmfmac4366b-pcie.bin" +#define BRCMF_PCIE_4366_NVRAM_NAME "brcm/brcmfmac4366b-pcie.txt" #define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ @@ -74,6 +80,8 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_REG_INTMASK 0x94 #define BRCMF_PCIE_REG_SBMBX 0x98 +#define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC + #define BRCMF_PCIE_PCIE2REG_INTMASK 0x24 #define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48 #define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C @@ -192,12 +200,18 @@ enum brcmf_pcie_state { MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4350_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4350_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); MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4365_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4365_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4366_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4366_NVRAM_NAME); struct brcmf_pcie_console { @@ -466,6 +480,7 @@ brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid) static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) { + struct brcmf_core *core; u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD, BRCMF_PCIE_CFGREG_PM_CSR, BRCMF_PCIE_CFGREG_MSI_CAP, @@ -484,32 +499,38 @@ static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) if (!devinfo->ci) return; + /* Disable ASPM */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, - BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL); - lsc = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA); + pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + &lsc); val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, val); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + val); + /* Watchdog reset */ brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON); WRITECC32(devinfo, watchdog, 4); msleep(100); + /* Restore ASPM */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, - BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, lsc); - - brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) { - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, - cfg_offset[i]); - val = brcmf_pcie_read_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_CONFIGDATA); - brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n", - cfg_offset[i], val); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, - val); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + lsc); + + core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2); + if (core->rev <= 13) { + for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) { + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGADDR, + cfg_offset[i]); + val = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGDATA); + brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n", + cfg_offset[i], val); + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGDATA, + val); + } } } @@ -519,8 +540,6 @@ static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) u32 config; brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) - brcmf_pcie_reset_device(devinfo); /* BAR1 window may not be sized properly */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0); @@ -644,7 +663,7 @@ static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET; console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr); - brcmf_dbg(PCIE, "Console: base %x, buf %x, size %d\n", + brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n", console->base_addr, console->buf_addr, console->bufsize); } @@ -656,6 +675,9 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) u8 ch; u32 newidx; + if (!BRCMF_FWCON_ON()) + return; + console = &devinfo->shared.console; addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET; newidx = brcmf_pcie_read_tcm32(devinfo, addr); @@ -677,7 +699,7 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) } if (ch == '\n') { console->log_str[console->log_idx] = 0; - brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str); + pr_debug("CONSOLE: %s", console->log_str); console->log_idx = 0; } } @@ -1408,6 +1430,10 @@ 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_4350_CHIP_ID: + fw_name = BRCMF_PCIE_4350_FW_NAME; + nvram_name = BRCMF_PCIE_4350_NVRAM_NAME; + break; case BRCM_CC_4356_CHIP_ID: fw_name = BRCMF_PCIE_4356_FW_NAME; nvram_name = BRCMF_PCIE_4356_NVRAM_NAME; @@ -1422,6 +1448,14 @@ static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) fw_name = BRCMF_PCIE_4358_FW_NAME; nvram_name = BRCMF_PCIE_4358_NVRAM_NAME; break; + case BRCM_CC_4365_CHIP_ID: + fw_name = BRCMF_PCIE_4365_FW_NAME; + nvram_name = BRCMF_PCIE_4365_NVRAM_NAME; + break; + case BRCM_CC_4366_CHIP_ID: + fw_name = BRCMF_PCIE_4366_FW_NAME; + nvram_name = BRCMF_PCIE_4366_NVRAM_NAME; + break; default: brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip); return -ENODEV; @@ -1633,6 +1667,23 @@ static int brcmf_pcie_buscoreprep(void *ctx) } +static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + u32 val; + + devinfo->ci = chip; + brcmf_pcie_reset_device(devinfo); + + val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + if (val != 0xffffffff) + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + val); + + return 0; +} + + static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, u32 rstvec) { @@ -1644,6 +1695,7 @@ static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { .prepare = brcmf_pcie_buscoreprep, + .reset = brcmf_pcie_buscore_reset, .activate = brcmf_pcie_buscore_activate, .read32 = brcmf_pcie_buscore_read32, .write32 = brcmf_pcie_buscore_write32, @@ -1811,7 +1863,6 @@ brcmf_pcie_remove(struct pci_dev *pdev) brcmf_pcie_intr_disable(devinfo); brcmf_detach(&pdev->dev); - brcmf_pcie_reset_device(devinfo); kfree(bus->bus_priv.pcie); kfree(bus->msgbuf->flowrings); @@ -1929,6 +1980,7 @@ 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_4350_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), @@ -1937,6 +1989,12 @@ static struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID), { /* end: all zeroes */ } }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/brcm80211/brcmfmac/proto.h index 971172ff686c..d55119d36755 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.h @@ -24,8 +24,8 @@ enum proto_addr_mode { struct brcmf_proto { - int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, - struct sk_buff *skb); + int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, + struct sk_buff *skb, struct brcmf_if **ifp); int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len); int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, @@ -46,9 +46,19 @@ int brcmf_proto_attach(struct brcmf_pub *drvr); void brcmf_proto_detach(struct brcmf_pub *drvr); static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, - u8 *ifidx, struct sk_buff *skb) + struct sk_buff *skb, + struct brcmf_if **ifp) { - return drvr->proto->hdrpull(drvr, do_fws, ifidx, skb); + struct brcmf_if *tmp = NULL; + + /* assure protocol is always called with + * non-null initialized pointer. + */ + if (ifp) + *ifp = NULL; + else + ifp = &tmp; + return drvr->proto->hdrpull(drvr, do_fws, skb, ifp); } static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index f990e3d0e696..7f574f26cdef 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -15,6 +15,7 @@ */ #include <linux/types.h> +#include <linux/atomic.h> #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/printk.h> @@ -123,6 +124,7 @@ struct rte_console { #define BRCMF_FIRSTREAD (1 << 6) +#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */ /* SBSDIO_DEVICE_CTL */ @@ -3204,6 +3206,8 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) if (IS_ERR_OR_NULL(dentry)) return; + bus->console_interval = BRCMF_CONSOLE; + brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read); brcmf_debugfs_add_entry(drvr, "counters", brcmf_debugfs_sdio_count_read); @@ -3613,7 +3617,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) } #ifdef DEBUG /* Poll for console output periodically */ - if (bus->sdiodev->state == BRCMF_SDIOD_DATA && + if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() && bus->console_interval != 0) { bus->console.count += BRCMF_WD_POLL_MS; if (bus->console.count >= bus->console_interval) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index d2c5747e3ac9..bec2dc1ca2e4 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -820,7 +820,7 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct brcms_info *wl = hw->priv; struct scb *scb = &wl->wlc->pri_scb; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 9728be0e704b..218cbc8bf3a7 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -4585,7 +4585,7 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core, wlc_hw->machwcap_backup = wlc_hw->machwcap; /* init tx fifo size */ - WARN_ON((wlc_hw->corerev - XMTFIFOTBL_STARTREV) < 0 || + WARN_ON(wlc_hw->corerev < XMTFIFOTBL_STARTREV || (wlc_hw->corerev - XMTFIFOTBL_STARTREV) > ARRAY_SIZE(xmtfifo_sz)); wlc_hw->xmtfifo_sz = diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 7a6daa37dc6b..d823734a4713 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -39,6 +39,7 @@ #define BRCM_CC_4339_CHIP_ID 0x4339 #define BRCM_CC_43430_CHIP_ID 43430 #define BRCM_CC_4345_CHIP_ID 0x4345 +#define BRCM_CC_4350_CHIP_ID 0x4350 #define BRCM_CC_4354_CHIP_ID 0x4354 #define BRCM_CC_4356_CHIP_ID 0x4356 #define BRCM_CC_43566_CHIP_ID 43566 @@ -47,6 +48,8 @@ #define BRCM_CC_43570_CHIP_ID 43570 #define BRCM_CC_4358_CHIP_ID 0x4358 #define BRCM_CC_43602_CHIP_ID 43602 +#define BRCM_CC_4365_CHIP_ID 0x4365 +#define BRCM_CC_4366_CHIP_ID 0x4366 /* USB Device IDs */ #define BRCM_USB_43143_DEVICE_ID 0xbd1e @@ -56,6 +59,7 @@ #define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc /* PCIE Device IDs */ +#define BRCM_PCIE_4350_DEVICE_ID 0x43a3 #define BRCM_PCIE_4354_DEVICE_ID 0x43df #define BRCM_PCIE_4356_DEVICE_ID 0x43ec #define BRCM_PCIE_43567_DEVICE_ID 0x43d3 @@ -65,6 +69,13 @@ #define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb #define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc #define BRCM_PCIE_43602_RAW_DEVICE_ID 43602 +#define BRCM_PCIE_4365_DEVICE_ID 0x43ca +#define BRCM_PCIE_4365_2G_DEVICE_ID 0x43cb +#define BRCM_PCIE_4365_5G_DEVICE_ID 0x43cc +#define BRCM_PCIE_4366_DEVICE_ID 0x43c3 +#define BRCM_PCIE_4366_2G_DEVICE_ID 0x43c4 +#define BRCM_PCIE_4366_5G_DEVICE_ID 0x43c5 + /* brcmsmac IDs */ #define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index b86500b4418f..95a7fdb3cc1c 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -2137,7 +2137,7 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { /* Aggregation is implemented fully in firmware, * including block ack negotiation. Do not allow diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h index b7e386b7662b..bebb3379017f 100644 --- a/drivers/net/wireless/cw1200/sta.h +++ b/drivers/net/wireless/cw1200/sta.h @@ -111,7 +111,7 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size); + u8 buf_size, bool amsdu); void cw1200_suspend_resume(struct cw1200_common *priv, struct wsm_suspend_resume *arg); diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/ipw2x00/libipw_rx.c index a6877dd6ba73..cef7f7d79cd9 100644 --- a/drivers/net/wireless/ipw2x00/libipw_rx.c +++ b/drivers/net/wireless/ipw2x00/libipw_rx.c @@ -1091,8 +1091,6 @@ static const char *get_info_element_string(u16 id) MFIE_STRING(TIM); MFIE_STRING(IBSS_PARAMS); MFIE_STRING(COUNTRY); - MFIE_STRING(HP_PARAMS); - MFIE_STRING(HP_TABLE); MFIE_STRING(REQUEST); MFIE_STRING(CHALLENGE); MFIE_STRING(PWR_CONSTRAINT); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 44fa422f255e..6656215a13a9 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5984,7 +5984,7 @@ int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct il_priv *il = hw->priv; int ret = -EINVAL; diff --git a/drivers/net/wireless/iwlegacy/4965.h b/drivers/net/wireless/iwlegacy/4965.h index 3a57f71b8ed5..8ab8706f9422 100644 --- a/drivers/net/wireless/iwlegacy/4965.h +++ b/drivers/net/wireless/iwlegacy/4965.h @@ -184,7 +184,7 @@ void il4965_mac_update_tkip_key(struct ieee80211_hw *hw, int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size); + u8 buf_size, bool amsdu); int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 453f7c315ab5..b3ad34e8bf5a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -731,7 +731,7 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int ret = -EINVAL; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index d7275a5f7f00..8443e14101cf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -820,7 +820,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size) + u16 *ssn, u8 buf_size, bool amsdu) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 064c100e45fe..f0728b784edb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -629,6 +629,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) kfree(mvm->d3_resume_sram); if (mvm->nd_config) { kfree(mvm->nd_config->match_sets); + kfree(mvm->nd_config->scan_plans); kfree(mvm->nd_config); mvm->nd_config = NULL; } diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 4a1f9af63bf0..cee4f267ca66 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -1271,12 +1271,12 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, params.type = iwl_mvm_get_scan_type(mvm, vif, ¶ms); - if (req->interval > U16_MAX) { + if (req->scan_plans[0].interval > U16_MAX) { IWL_DEBUG_SCAN(mvm, "interval value is > 16-bits, set to max possible\n"); params.interval = U16_MAX; } else { - params.interval = req->interval / MSEC_PER_SEC; + params.interval = req->scan_plans[0].interval; } /* In theory, LMAC scans can handle a 32-bit delay, but since diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 520bef80747f..ee46f4647fbc 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1819,7 +1819,7 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { switch (action) { case IEEE80211_AMPDU_TX_START: @@ -2190,9 +2190,8 @@ static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb, struct genl_info *info) { if (info) - genl_notify(&hwsim_genl_family, mcast_skb, - genl_info_net(info), info->snd_portid, - HWSIM_MCGRP_CONFIG, info->nlhdr, GFP_KERNEL); + genl_notify(&hwsim_genl_family, mcast_skb, info, + HWSIM_MCGRP_CONFIG, GFP_KERNEL); else genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0, HWSIM_MCGRP_CONFIG, GFP_KERNEL); diff --git a/drivers/net/wireless/mediatek/mt7601u/main.c b/drivers/net/wireless/mediatek/mt7601u/main.c index 169384b48b27..f715eee39851 100644 --- a/drivers/net/wireless/mediatek/mt7601u/main.c +++ b/drivers/net/wireless/mediatek/mt7601u/main.c @@ -335,7 +335,8 @@ static int mt7601u_set_rts_threshold(struct ieee80211_hw *hw, u32 value) static int mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) + struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, + bool amsdu) { struct mt7601u_dev *dev = hw->priv; struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index f7c717253a66..aa498e0d2204 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -173,7 +173,6 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, int pad = 0, aggr_num = 0, ret; struct mwifiex_tx_param tx_param; struct txpd *ptx_pd = NULL; - struct timeval tv; int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN; skb_src = skb_peek(&pra_list->skb_head); @@ -202,9 +201,9 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT; skb_aggr->priority = skb_src->priority; + skb_aggr->tstamp = skb_src->tstamp; - do_gettimeofday(&tv); - skb_aggr->tstamp = timeval_to_ktime(tv); + skb_aggr->tstamp = ktime_get_real(); do { /* Check if AMSDU can accommodate this MSDU */ @@ -258,8 +257,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, } if (adapter->iface_type == MWIFIEX_USB) { - adapter->data_sent = true; - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, skb_aggr, NULL); } else { if (skb_src) @@ -299,16 +297,12 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); break; case -1: - if (adapter->iface_type != MWIFIEX_PCIE) - adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n", __func__, ret); adapter->dbg.num_tx_host_to_card_failure++; mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); return 0; case -EINPROGRESS: - if (adapter->iface_type != MWIFIEX_PCIE) - adapter->data_sent = false; break; case 0: mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index 2906cd543532..b3970a8c9e48 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -615,10 +615,10 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, ((end_win > start_win) && ((seq_num > end_win) || (seq_num < start_win)))) { end_win = seq_num; - if (((seq_num - win_size) + 1) >= 0) + if (((end_win - win_size) + 1) >= 0) start_win = (end_win - win_size) + 1; else - start_win = (MAX_TID_VALUE - (win_size - seq_num)) + 1; + start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1; mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); } diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index ff63cb5632eb..30cbafbd17c6 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1994,8 +1994,10 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) CFG80211_BSS_FTYPE_UNKNOWN, bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, 0, ie_buf, ie_len, 0, GFP_KERNEL); - cfg80211_put_bss(priv->wdev.wiphy, bss); - memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN); + if (bss) { + cfg80211_put_bss(priv->wdev.wiphy, bss); + ether_addr_copy(priv->cfg_bssid, bss_info.bssid); + } return 0; } @@ -2859,14 +2861,14 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: - adapter->curr_iface_comb.sta_intf++; + adapter->curr_iface_comb.sta_intf--; break; case NL80211_IFTYPE_AP: - adapter->curr_iface_comb.uap_intf++; + adapter->curr_iface_comb.uap_intf--; break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: - adapter->curr_iface_comb.p2p_intf++; + adapter->curr_iface_comb.p2p_intf--; break; default: mwifiex_dbg(adapter, ERROR, diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index 5a0636d43a1b..5583856fc5c4 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -731,7 +731,7 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, (struct mwifiex_private *) file->private_data; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *) addr; - int pos = 0, ret = 0, i; + int pos, ret, i; u8 value[MAX_EEPROM_DATA]; if (!buf) @@ -739,7 +739,7 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, if (saved_offset == -1) { /* No command has been given */ - pos += snprintf(buf, PAGE_SIZE, "0"); + pos = snprintf(buf, PAGE_SIZE, "0"); goto done; } @@ -748,17 +748,17 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, (u16) saved_bytes, value); if (ret) { ret = -EINVAL; - goto done; + goto out_free; } - pos += snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); + pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); for (i = 0; i < saved_bytes; i++) - pos += snprintf(buf + strlen(buf), PAGE_SIZE, "%d ", value[i]); - - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]); done: + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); +out_free: free_page(addr); return ret; } diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 3ec2ac82e394..0e6f029458d5 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -104,6 +104,7 @@ enum KEY_TYPE_ID { enum mwifiex_usb_ep { MWIFIEX_USB_EP_CMD_EVENT = 1, MWIFIEX_USB_EP_DATA = 2, + MWIFIEX_USB_EP_DATA_CH2 = 3, }; enum MWIFIEX_802_11_PRIVACY_FILTER { @@ -173,6 +174,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) #define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) #define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183) +#define TLV_TYPE_MC_GROUP_INFO (PROPRIETARY_TLV_BASE_ID + 184) #define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194) #define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197) #define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199) @@ -1984,6 +1986,22 @@ struct mwifiex_ie_types_multi_chan_info { u8 tlv_buffer[0]; } __packed; +struct mwifiex_ie_types_mc_group_info { + struct mwifiex_ie_types_header header; + u8 chan_group_id; + u8 chan_buf_weight; + u8 band_config; + u8 chan_num; + u32 chan_time; + u32 reserved; + union { + u8 sdio_func_num; + u8 usb_ep_num; + } hid_num; + u8 intf_num; + u8 bss_type_numlist[0]; +} __packed; + struct meas_rpt_map { u8 rssi:3; u8 unmeasured:1; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 5d3ae63baea4..de74a7773fb6 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -78,6 +78,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv) priv->media_connected = false; eth_broadcast_addr(priv->curr_addr); priv->port_open = false; + priv->usb_port = MWIFIEX_USB_EP_DATA; priv->pkt_tx_ctrl = 0; priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; priv->data_rate = 0; /* Initially indicate the rate as auto */ diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 278dc94eaecb..6e3faa74389c 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -294,9 +294,15 @@ process_start: /* We have tried to wakeup the card already */ if (adapter->pm_wakeup_fw_try) break; - if (adapter->ps_state != PS_STATE_AWAKE || - adapter->tx_lock_flag) + if (adapter->ps_state != PS_STATE_AWAKE) break; + if (adapter->tx_lock_flag) { + if (adapter->iface_type == MWIFIEX_USB) { + if (!adapter->usb_mc_setup) + break; + } else + break; + } if ((!adapter->scan_chan_gap_enabled && adapter->scan_processing) || adapter->data_sent || @@ -345,11 +351,18 @@ process_start: */ if ((adapter->ps_state == PS_STATE_SLEEP) || (adapter->ps_state == PS_STATE_PRE_SLEEP) || - (adapter->ps_state == PS_STATE_SLEEP_CFM) || - adapter->tx_lock_flag){ + (adapter->ps_state == PS_STATE_SLEEP_CFM)) { continue; } + if (adapter->tx_lock_flag) { + if (adapter->iface_type == MWIFIEX_USB) { + if (!adapter->usb_mc_setup) + continue; + } else + continue; + } + if (!adapter->cmd_sent && !adapter->curr_cmd && mwifiex_is_send_cmd_allowed (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { @@ -359,6 +372,13 @@ process_start: } } + /** If USB Multi channel setup ongoing, + * wait for ready to tx data. + */ + if (adapter->iface_type == MWIFIEX_USB && + adapter->usb_mc_setup) + continue; + if ((adapter->scan_chan_gap_enabled || !adapter->scan_processing) && !adapter->data_sent && @@ -928,6 +948,32 @@ mwifiex_tx_timeout(struct net_device *dev) } } +void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + struct mwifiex_private *priv; + u16 tx_buf_size; + int i, ret; + + card->mc_resync_flag = true; + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + if (atomic_read(&card->port[i].tx_data_urb_pending)) { + mwifiex_dbg(adapter, WARN, "pending data urb in sys\n"); + return; + } + } + + card->mc_resync_flag = false; + tx_buf_size = 0xffff; + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, + HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false); + if (ret) + mwifiex_dbg(adapter, ERROR, + "send reconfig tx buf size cmd err\n"); +} +EXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync); + void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter) { void *p; @@ -963,8 +1009,10 @@ void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter) cardp = (struct usb_card_rec *)adapter->card; p += sprintf(p, "tx_cmd_urb_pending = %d\n", atomic_read(&cardp->tx_cmd_urb_pending)); - p += sprintf(p, "tx_data_urb_pending = %d\n", - atomic_read(&cardp->tx_data_urb_pending)); + p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n", + atomic_read(&cardp->port[0].tx_data_urb_pending)); + p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n", + atomic_read(&cardp->port[1].tx_data_urb_pending)); p += sprintf(p, "rx_cmd_urb_pending = %d\n", atomic_read(&cardp->rx_cmd_urb_pending)); p += sprintf(p, "rx_data_urb_pending = %d\n", @@ -1447,6 +1495,26 @@ exit_sem_err: } EXPORT_SYMBOL_GPL(mwifiex_remove_card); +void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (!adapter->dev || !(adapter->debug_mask & mask)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + dev_info(adapter->dev, "%pV", &vaf); + + va_end(args); +} +EXPORT_SYMBOL_GPL(_mwifiex_dbg); + /* * This function initializes the module. * diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 6b9512140e7a..3959f1c97f4e 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -48,6 +48,9 @@ extern const char driver_version[]; +struct mwifiex_adapter; +struct mwifiex_private; + enum { MWIFIEX_ASYNC_CMD, MWIFIEX_SYNC_CMD @@ -180,12 +183,11 @@ enum MWIFIEX_DEBUG_LEVEL { MWIFIEX_DBG_FATAL | \ MWIFIEX_DBG_ERROR) -#define mwifiex_dbg(adapter, dbg_mask, fmt, args...) \ -do { \ - if ((adapter)->debug_mask & MWIFIEX_DBG_##dbg_mask) \ - if ((adapter)->dev) \ - dev_info((adapter)->dev, fmt, ## args); \ -} while (0) +__printf(3, 4) +void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, + const char *fmt, ...); +#define mwifiex_dbg(adapter, mask, fmt, ...) \ + _mwifiex_dbg(adapter, MWIFIEX_DBG_##mask, fmt, ##__VA_ARGS__) #define DEBUG_DUMP_DATA_MAX_LEN 128 #define mwifiex_dbg_dump(adapter, dbg_mask, str, buf, len) \ @@ -506,9 +508,6 @@ enum mwifiex_iface_work_flags { MWIFIEX_IFACE_WORK_CARD_RESET, }; -struct mwifiex_adapter; -struct mwifiex_private; - struct mwifiex_private { struct mwifiex_adapter *adapter; u8 bss_type; @@ -520,6 +519,7 @@ struct mwifiex_private { u8 curr_addr[ETH_ALEN]; u8 media_connected; u8 port_open; + u8 usb_port; u32 num_tx_timeout; /* track consecutive timeout */ u8 tx_timeout_cnt; @@ -816,6 +816,8 @@ struct mwifiex_if_ops { 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 *); + void (*multi_port_resync)(struct mwifiex_adapter *); + bool (*is_port_ready)(struct mwifiex_private *); }; struct mwifiex_adapter { @@ -861,6 +863,8 @@ struct mwifiex_adapter { u8 more_task_flag; u16 tx_buf_size; u16 curr_tx_buf_size; + /* sdio single port rx aggregation capability */ + bool host_disable_sdio_rx_aggr; bool sdio_rx_aggr_enable; u16 sdio_rx_block_size; u32 ioport; @@ -988,6 +992,8 @@ struct mwifiex_adapter { u8 coex_rx_win_size; bool drcs_enabled; u8 active_scan_triggered; + bool usb_mc_status; + bool usb_mc_setup; }; void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter); @@ -1561,6 +1567,7 @@ void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, struct sk_buff *event); void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, struct sk_buff *event_skb); +void mwifiex_multi_chan_resync(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 408b68460716..21192b6f9c64 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -1815,7 +1815,6 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, if (!card->evt_buf_list[rdptr]) { skb_push(skb, INTF_HEADER_LEN); skb_put(skb, MAX_EVENT_SIZE - skb->len); - memset(skb->data, 0, MAX_EVENT_SIZE); if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE, PCI_DMA_FROMDEVICE)) diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 5847863a2d6b..c20017ced566 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1839,14 +1839,18 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, bssid, timestamp, cap_info_bitmap, beacon_period, ie_buf, ie_len, rssi, GFP_KERNEL); - bss_priv = (struct mwifiex_bss_priv *)bss->priv; - bss_priv->band = band; - bss_priv->fw_tsf = fw_tsf; - if (priv->media_connected && - !memcmp(bssid, priv->curr_bss_params.bss_descriptor - .mac_address, ETH_ALEN)) - mwifiex_update_curr_bss_params(priv, bss); - cfg80211_put_bss(priv->wdev.wiphy, bss); + if (bss) { + bss_priv = (struct mwifiex_bss_priv *)bss->priv; + bss_priv->band = band; + bss_priv->fw_tsf = fw_tsf; + if (priv->media_connected && + !memcmp(bssid, priv->curr_bss_params. + bss_descriptor.mac_address, + ETH_ALEN)) + mwifiex_update_curr_bss_params(priv, + bss); + cfg80211_put_bss(priv->wdev.wiphy, bss); + } if ((chan->flags & IEEE80211_CHAN_RADAR) || (chan->flags & IEEE80211_CHAN_NO_IR)) { @@ -1889,7 +1893,7 @@ mwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv) u8 id = 0; struct mwifiex_user_scan_cfg *user_scan_cfg; - if (adapter->active_scan_triggered) { + if (adapter->active_scan_triggered || !priv->scan_request) { adapter->active_scan_triggered = false; return 0; } diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 5d05c6fe6429..78a8474e1a3d 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -1606,8 +1606,9 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) (rx_len + MWIFIEX_SDIO_BLOCK_SIZE - 1) / MWIFIEX_SDIO_BLOCK_SIZE; if (rx_len <= INTF_HEADER_LEN || - (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > - card->mpa_rx.buf_size) { + (card->mpa_rx.enabled && + ((rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > + card->mpa_rx.buf_size))) { mwifiex_dbg(adapter, ERROR, "invalid rx_len=%d\n", rx_len); @@ -1925,6 +1926,8 @@ error: if (ret) { kfree(card->mpa_tx.buf); kfree(card->mpa_rx.buf); + card->mpa_tx.buf_size = 0; + card->mpa_rx.buf_size = 0; } return ret; @@ -2055,16 +2058,26 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) ret = mwifiex_alloc_sdio_mpa_buffers(adapter, card->mp_tx_agg_buf_size, card->mp_rx_agg_buf_size); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "failed to alloc sdio mp-a buffers\n"); - kfree(card->mp_regs); - return -1; + + /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */ + if (ret && (card->mp_tx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX || + card->mp_rx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX)) { + /* Disable rx single port aggregation */ + adapter->host_disable_sdio_rx_aggr = true; + + ret = mwifiex_alloc_sdio_mpa_buffers + (adapter, MWIFIEX_MP_AGGR_BUF_SIZE_32K, + MWIFIEX_MP_AGGR_BUF_SIZE_32K); + if (ret) { + /* Disable multi port aggregation */ + card->mpa_tx.enabled = 0; + card->mpa_rx.enabled = 0; + } } adapter->auto_tdls = card->can_auto_tdls; adapter->ext_scan = card->can_ext_scan; - return ret; + return 0; } /* diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index a49a80dd773e..504b321301ec 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -2125,7 +2125,8 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) /** Set SDIO Single Port RX Aggr Info */ if (priv->adapter->iface_type == MWIFIEX_SDIO && - ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info)) { + ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) && + !priv->adapter->host_disable_sdio_rx_aggr) { sdio_sp_rx_aggr_enable = true; ret = mwifiex_send_cmd(priv, HostCmd_CMD_SDIO_SP_RX_AGGR_CFG, diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 87b69d8ad120..d0961635c7b3 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -1128,6 +1128,17 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, ret = mwifiex_ret_11n_addba_resp(priv, resp); break; case HostCmd_CMD_RECONFIGURE_TX_BUFF: + if (0xffff == (u16)le16_to_cpu(resp->params.tx_buf.buff_size)) { + if (adapter->iface_type == MWIFIEX_USB && + adapter->usb_mc_setup) { + if (adapter->if_ops.multi_port_resync) + adapter->if_ops. + multi_port_resync(adapter); + adapter->usb_mc_setup = false; + adapter->tx_lock_flag = false; + } + break; + } adapter->tx_buf_size = (u16) le16_to_cpu(resp->params. tx_buf.buff_size); adapter->tx_buf_size = (adapter->tx_buf_size diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 3d18c585e543..ff3ee9dfbbd5 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -313,24 +313,78 @@ void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, struct sk_buff *event_skb) { struct mwifiex_ie_types_multi_chan_info *chan_info; - u16 status; + struct mwifiex_ie_types_mc_group_info *grp_info; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_header *tlv; + u16 tlv_buf_left, tlv_type, tlv_len; + int intf_num, bss_type, bss_num, i; + struct mwifiex_private *intf_priv; + tlv_buf_left = event_skb->len - sizeof(u32); chan_info = (void *)event_skb->data + sizeof(u32); - if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO) { - mwifiex_dbg(priv->adapter, ERROR, + if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO || + tlv_buf_left < sizeof(struct mwifiex_ie_types_multi_chan_info)) { + mwifiex_dbg(adapter, ERROR, "unknown TLV in chan_info event\n"); return; } - status = le16_to_cpu(chan_info->status); + adapter->usb_mc_status = le16_to_cpu(chan_info->status); + mwifiex_dbg(adapter, EVENT, "multi chan operation %s\n", + adapter->usb_mc_status ? "started" : "over"); - if (status) { - mwifiex_dbg(priv->adapter, EVENT, - "multi-channel operation started\n"); - } else { - mwifiex_dbg(priv->adapter, EVENT, - "multi-channel operation over\n"); + tlv_buf_left -= sizeof(struct mwifiex_ie_types_multi_chan_info); + tlv = (struct mwifiex_ie_types_header *)chan_info->tlv_buffer; + + while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_len = le16_to_cpu(tlv->len); + if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) > + tlv_buf_left) { + mwifiex_dbg(adapter, ERROR, "wrong tlv: tlvLen=%d,\t" + "tlvBufLeft=%d\n", tlv_len, tlv_buf_left); + break; + } + if (tlv_type != TLV_TYPE_MC_GROUP_INFO) { + mwifiex_dbg(adapter, ERROR, "wrong tlv type: 0x%x\n", + tlv_type); + break; + } + + grp_info = (struct mwifiex_ie_types_mc_group_info *)tlv; + intf_num = grp_info->intf_num; + for (i = 0; i < intf_num; i++) { + bss_type = grp_info->bss_type_numlist[i] >> 4; + bss_num = grp_info->bss_type_numlist[i] & BSS_NUM_MASK; + intf_priv = mwifiex_get_priv_by_id(adapter, bss_num, + bss_type); + if (!intf_priv) { + mwifiex_dbg(adapter, ERROR, + "Invalid bss_type bss_num\t" + "in multi channel event\n"); + continue; + } + if (adapter->iface_type == MWIFIEX_USB) { + u8 ep; + + ep = grp_info->hid_num.usb_ep_num; + if (ep == MWIFIEX_USB_EP_DATA || + ep == MWIFIEX_USB_EP_DATA_CH2) + intf_priv->usb_port = ep; + } + } + + tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) + + tlv_len; + tlv = (void *)((u8 *)tlv + tlv_len + + sizeof(struct mwifiex_ie_types_header)); + } + + if (adapter->iface_type == MWIFIEX_USB) { + adapter->tx_lock_flag = true; + adapter->usb_mc_setup = true; + mwifiex_multi_chan_resync(adapter); } } @@ -562,7 +616,9 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->tx_lock_flag = false; if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { if (mwifiex_check_last_packet_indication(priv)) { - if (adapter->data_sent) { + if (adapter->data_sent || + (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv))) { adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index 355ac5904fac..f6683ea6bd5d 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -153,6 +153,10 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) if (adapter->data_sent) return -1; + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + return -1; + skb = dev_alloc_skb(data_len); if (!skb) return -1; @@ -174,7 +178,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) local_tx_pd->bss_type = priv->bss_type; if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, skb, NULL); } else { skb_push(skb, INTF_HEADER_LEN); @@ -191,7 +195,6 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) adapter->dbg.num_tx_host_to_card_failure++; break; case -1: - adapter->data_sent = false; dev_kfree_skb_any(skb); mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: ret=%d\n", diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index b3e163de9899..9275f9c3f869 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -204,6 +204,12 @@ mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, return -1; } + if (!(le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info))) { + mwifiex_dbg(priv->adapter, WARN, + "TDLS peer doesn't support ht capabilities\n"); + return 0; + } + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2); *pos++ = WLAN_EID_HT_OPERATION; *pos++ = sizeof(struct ieee80211_ht_operation); @@ -252,6 +258,12 @@ static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, return -1; } + if (!(le32_to_cpu(sta_ptr->tdls_cap.vhtcap.vht_cap_info))) { + mwifiex_dbg(adapter, WARN, + "TDLS peer doesn't support vht capabilities\n"); + return 0; + } + if (!mwifiex_is_bss_in_11ac_mode(priv)) { if (sta_ptr->tdls_cap.extcap.ext_capab[7] & WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 8b1e5b5d47fe..bf6182b646a5 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -115,9 +115,8 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) local_tx_pd = (struct txpd *)(head_ptr + hroom); if (adapter->iface_type == MWIFIEX_USB) { - adapter->data_sent = true; ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_USB_EP_DATA, + priv->usb_port, skb, NULL); } else { ret = adapter->if_ops.host_to_card(adapter, @@ -130,7 +129,7 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, switch (ret) { case -ENOSR: - mwifiex_dbg(adapter, ERROR, "data: -ENOSR is returned\n"); + mwifiex_dbg(adapter, DATA, "data: -ENOSR is returned\n"); break; case -EBUSY: if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && @@ -142,8 +141,6 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); break; case -1: - if (adapter->iface_type != MWIFIEX_PCIE) - adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "mwifiex_write_data_async failed: 0x%X\n", ret); @@ -151,8 +148,6 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, 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); @@ -193,9 +188,8 @@ static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, } if (adapter->iface_type == MWIFIEX_USB) { - adapter->data_sent = true; ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_USB_EP_DATA, + priv->usb_port, skb, NULL); } else { ret = adapter->if_ops.host_to_card(adapter, @@ -222,16 +216,12 @@ static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); break; case -1: - if (adapter->iface_type != MWIFIEX_PCIE) - adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "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); @@ -306,9 +296,6 @@ int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, if (!priv) goto done; - if (adapter->iface_type == MWIFIEX_USB) - adapter->data_sent = false; - mwifiex_set_trans_start(priv->netdev); if (!status) { priv->stats.tx_packets++; diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index 46c972a650a4..078834cf1251 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -269,7 +269,9 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) adapter->tx_lock_flag = false; if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { if (mwifiex_check_last_packet_indication(priv)) { - if (adapter->data_sent) { + if (adapter->data_sent || + (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv))) { adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index 87667418af5f..74d5d7238633 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -31,7 +31,8 @@ */ static bool mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv, - struct list_head *ra_list_head) + struct list_head *ra_list_head, + int tid) { struct mwifiex_ra_list_tbl *ra_list; struct sk_buff *skb, *tmp; @@ -49,7 +50,10 @@ mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv, __skb_unlink(skb, &ra_list->skb_head); mwifiex_write_data_complete(adapter, skb, 0, -1); - atomic_dec(&priv->wmm.tx_pkts_queued); + if (ra_list->tx_paused) + priv->wmm.pkts_paused[tid]--; + else + atomic_dec(&priv->wmm.tx_pkts_queued); pkt_deleted = true; } if ((atomic_read(&adapter->pending_bridged_pkts) <= @@ -77,7 +81,7 @@ static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv) if (priv->del_list_idx == MAX_NUM_TID) priv->del_list_idx = 0; ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list; - if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list)) { + if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) { priv->del_list_idx++; break; } diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 5e789b2e06ea..9f5356ef0531 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -264,6 +264,8 @@ static void mwifiex_usb_tx_complete(struct urb *urb) struct urb_context *context = (struct urb_context *)(urb->context); struct mwifiex_adapter *adapter = context->adapter; struct usb_card_rec *card = adapter->card; + struct usb_tx_data_port *port; + int i; mwifiex_dbg(adapter, INFO, "%s: status: %d\n", __func__, urb->status); @@ -276,11 +278,22 @@ static void mwifiex_usb_tx_complete(struct urb *urb) } else { mwifiex_dbg(adapter, DATA, "%s: DATA\n", __func__); - atomic_dec(&card->tx_data_urb_pending); + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + if (context->ep == port->tx_data_ep) { + atomic_dec(&port->tx_data_urb_pending); + port->block_status = false; + break; + } + } + adapter->data_sent = false; mwifiex_write_data_complete(adapter, context->skb, 0, urb->status ? -1 : 0); } + if (card->mc_resync_flag) + mwifiex_multi_chan_resync(adapter); + mwifiex_queue_main_work(adapter); return; @@ -327,7 +340,8 @@ static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size) static void mwifiex_usb_free(struct usb_card_rec *card) { - int i; + struct usb_tx_data_port *port; + int i, j; if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) usb_kill_urb(card->rx_cmd.urb); @@ -345,9 +359,12 @@ static void mwifiex_usb_free(struct usb_card_rec *card) card->rx_data_list[i].urb = NULL; } - for (i = 0; i < MWIFIEX_TX_DATA_URB; i++) { - usb_free_urb(card->tx_data_list[i].urb); - card->tx_data_list[i].urb = NULL; + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + usb_free_urb(port->tx_data_list[j].urb); + port->tx_data_list[j].urb = NULL; + } } usb_free_urb(card->tx_cmd.urb); @@ -437,8 +454,18 @@ static int mwifiex_usb_probe(struct usb_interface *intf, pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", le16_to_cpu(epd->wMaxPacketSize), epd->bEndpointAddress); - card->tx_data_ep = usb_endpoint_num(epd); - atomic_set(&card->tx_data_urb_pending, 0); + card->port[0].tx_data_ep = usb_endpoint_num(epd); + atomic_set(&card->port[0].tx_data_urb_pending, 0); + } + if (usb_endpoint_dir_out(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA_CH2 && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk OUT chan2:\t" + "max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->port[1].tx_data_ep = usb_endpoint_num(epd); + atomic_set(&card->port[1].tx_data_urb_pending, 0); } if (usb_endpoint_dir_out(epd) && usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && @@ -480,7 +507,8 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) { struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; - int i; + struct usb_tx_data_port *port; + int i, j; if (!card || !card->adapter) { pr_err("%s: card or card->adapter is NULL\n", __func__); @@ -511,9 +539,13 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) if (card->rx_data_list[i].urb) usb_kill_urb(card->rx_data_list[i].urb); - for (i = 0; i < MWIFIEX_TX_DATA_URB; i++) - if (card->tx_data_list[i].urb) - usb_kill_urb(card->tx_data_list[i].urb); + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + if (port->tx_data_list[j].urb) + usb_kill_urb(port->tx_data_list[j].urb); + } + } if (card->tx_cmd.urb) usb_kill_urb(card->tx_cmd.urb); @@ -625,7 +657,8 @@ static struct usb_driver mwifiex_usb_driver = { static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) { struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - int i; + struct usb_tx_data_port *port; + int i, j; card->tx_cmd.adapter = adapter; card->tx_cmd.ep = card->tx_cmd_ep; @@ -637,17 +670,25 @@ static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) return -ENOMEM; } - card->tx_data_ix = 0; - - for (i = 0; i < MWIFIEX_TX_DATA_URB; i++) { - card->tx_data_list[i].adapter = adapter; - card->tx_data_list[i].ep = card->tx_data_ep; - - card->tx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); - if (!card->tx_data_list[i].urb) { - mwifiex_dbg(adapter, ERROR, - "tx_data_list[] urb allocation failed\n"); - return -ENOMEM; + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + if (!port->tx_data_ep) + continue; + port->tx_data_ix = 0; + if (port->tx_data_ep == MWIFIEX_USB_EP_DATA) + port->block_status = false; + else + port->block_status = true; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + port->tx_data_list[j].adapter = adapter; + port->tx_data_list[j].ep = port->tx_data_ep; + port->tx_data_list[j].urb = + usb_alloc_urb(0, GFP_KERNEL); + if (!port->tx_data_list[j].urb) { + mwifiex_dbg(adapter, ERROR, + "urb allocation failed\n"); + return -ENOMEM; + } } } @@ -736,15 +777,89 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, return ret; } +static void mwifiex_usb_port_resync(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + u8 active_port = MWIFIEX_USB_EP_DATA; + struct mwifiex_private *priv = NULL; + int i; + + if (adapter->usb_mc_status) { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && + !priv->bss_started) || + (priv->bss_role == MWIFIEX_BSS_ROLE_STA && + !priv->media_connected)) + priv->usb_port = MWIFIEX_USB_EP_DATA; + } + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) + card->port[i].block_status = false; + } else { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && + priv->bss_started) || + (priv->bss_role == MWIFIEX_BSS_ROLE_STA && + priv->media_connected)) { + active_port = priv->usb_port; + break; + } + } + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + priv->usb_port = active_port; + } + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + if (active_port == card->port[i].tx_data_ep) + card->port[i].block_status = false; + else + card->port[i].block_status = true; + } + } +} + +static bool mwifiex_usb_is_port_ready(struct mwifiex_private *priv) +{ + struct usb_card_rec *card = priv->adapter->card; + int idx; + + for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { + if (priv->usb_port == card->port[idx].tx_data_ep) + return !card->port[idx].block_status; + } + + return false; +} + +static inline u8 mwifiex_usb_data_sent(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + int i; + + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) + if (!card->port[i].block_status) + return false; + + return true; +} + /* This function write a command/data packet to card. */ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, struct sk_buff *skb, struct mwifiex_tx_param *tx_param) { struct usb_card_rec *card = adapter->card; - struct urb_context *context; + struct urb_context *context = NULL; + struct usb_tx_data_port *port = NULL; u8 *data = (u8 *)skb->data; struct urb *tx_urb; + int idx, ret; if (adapter->is_suspended) { mwifiex_dbg(adapter, ERROR, @@ -757,19 +872,31 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, return -1; } - if (ep == card->tx_data_ep && - atomic_read(&card->tx_data_urb_pending) >= MWIFIEX_TX_DATA_URB) { - return -EBUSY; - } - mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep); if (ep == card->tx_cmd_ep) { context = &card->tx_cmd; } else { - if (card->tx_data_ix >= MWIFIEX_TX_DATA_URB) - card->tx_data_ix = 0; - context = &card->tx_data_list[card->tx_data_ix++]; + for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { + if (ep == card->port[idx].tx_data_ep) { + port = &card->port[idx]; + if (atomic_read(&port->tx_data_urb_pending) + >= MWIFIEX_TX_DATA_URB) { + port->block_status = true; + ret = -EBUSY; + goto done; + } + if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) + port->tx_data_ix = 0; + context = + &port->tx_data_list[port->tx_data_ix++]; + break; + } + } + if (!port) { + mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n"); + return -1; + } } context->adapter = adapter; @@ -786,7 +913,7 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, if (ep == card->tx_cmd_ep) atomic_inc(&card->tx_cmd_urb_pending); else - atomic_inc(&card->tx_data_urb_pending); + atomic_inc(&port->tx_data_urb_pending); if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { mwifiex_dbg(adapter, ERROR, @@ -794,22 +921,32 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, if (ep == card->tx_cmd_ep) { atomic_dec(&card->tx_cmd_urb_pending); } else { - atomic_dec(&card->tx_data_urb_pending); - if (card->tx_data_ix) - card->tx_data_ix--; + atomic_dec(&port->tx_data_urb_pending); + port->block_status = false; + if (port->tx_data_ix) + port->tx_data_ix--; else - card->tx_data_ix = MWIFIEX_TX_DATA_URB; + port->tx_data_ix = MWIFIEX_TX_DATA_URB; } return -1; } else { - if (ep == card->tx_data_ep && - atomic_read(&card->tx_data_urb_pending) == - MWIFIEX_TX_DATA_URB) - return -ENOSR; + if (ep != card->tx_cmd_ep && + atomic_read(&port->tx_data_urb_pending) == + MWIFIEX_TX_DATA_URB) { + port->block_status = true; + ret = -ENOSR; + goto done; + } } return -EINPROGRESS; + +done: + if (ep != card->tx_cmd_ep) + adapter->data_sent = mwifiex_usb_data_sent(adapter); + + return ret; } /* This function register usb device and initialize parameter. */ @@ -853,6 +990,9 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) break; } + adapter->usb_mc_status = false; + adapter->usb_mc_setup = false; + return 0; } @@ -1082,6 +1222,8 @@ static struct mwifiex_if_ops usb_ops = { .event_complete = mwifiex_usb_cmd_event_complete, .host_to_card = mwifiex_usb_host_to_card, .submit_rem_rx_urbs = mwifiex_usb_submit_rem_rx_urbs, + .multi_port_resync = mwifiex_usb_port_resync, + .is_port_ready = mwifiex_usb_is_port_ready, }; /* This function initializes the USB driver module. diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h index f0051f8c8981..bab10ee41923 100644 --- a/drivers/net/wireless/mwifiex/usb.h +++ b/drivers/net/wireless/mwifiex/usb.h @@ -40,6 +40,7 @@ #define USB8XXX_FW_READY 2 #define USB8XXX_FW_MAX_RETRY 3 +#define MWIFIEX_TX_DATA_PORT 2 #define MWIFIEX_TX_DATA_URB 6 #define MWIFIEX_RX_DATA_URB 6 #define MWIFIEX_USB_TIMEOUT 100 @@ -64,6 +65,14 @@ struct urb_context { u8 ep; }; +struct usb_tx_data_port { + u8 tx_data_ep; + u8 block_status; + atomic_t tx_data_urb_pending; + int tx_data_ix; + struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB]; +}; + struct usb_card_rec { struct mwifiex_adapter *adapter; struct usb_device *udev; @@ -75,14 +84,12 @@ struct usb_card_rec { u8 usb_boot_state; u8 rx_data_ep; atomic_t rx_data_urb_pending; - u8 tx_data_ep; u8 tx_cmd_ep; - atomic_t tx_data_urb_pending; atomic_t tx_cmd_urb_pending; int bulk_out_maxpktsize; struct urb_context tx_cmd; - int tx_data_ix; - struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB]; + u8 mc_resync_flag; + struct usb_tx_data_port port[MWIFIEX_TX_DATA_PORT]; }; struct fw_header { diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 173d3663c2e0..57c13ec3d3de 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -117,22 +117,15 @@ mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra) */ static u8 mwifiex_get_random_ba_threshold(void) { - u32 sec, usec; - struct timeval ba_tstamp; - u8 ba_threshold; - + u64 ns; /* setup ba_packet_threshold here random number between * [BA_SETUP_PACKET_OFFSET, * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1] */ + ns = ktime_get_ns(); + ns += (ns >> 32) + (ns >> 16); - do_gettimeofday(&ba_tstamp); - sec = (ba_tstamp.tv_sec & 0xFFFF) + (ba_tstamp.tv_sec >> 16); - usec = (ba_tstamp.tv_usec & 0xFFFF) + (ba_tstamp.tv_usec >> 16); - ba_threshold = (((sec << 16) + usec) % BA_SETUP_MAX_PACKET_THRESHOLD) - + BA_SETUP_PACKET_OFFSET; - - return ba_threshold; + return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET; } /* @@ -160,7 +153,6 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) ra_list->tdls_link = false; ra_list->ba_status = BA_SETUP_NONE; ra_list->amsdu_in_ampdu = false; - ra_list->tx_paused = false; if (!mwifiex_queuing_ra_based(priv)) { if (mwifiex_is_tdls_link_setup (mwifiex_get_tdls_link_status(priv, ra))) { @@ -173,6 +165,8 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) } else { spin_lock_irqsave(&priv->sta_list_spinlock, flags); node = mwifiex_get_sta_entry(priv, ra); + if (node) + ra_list->tx_paused = node->tx_pause; ra_list->is_11n_enabled = mwifiex_is_sta_11n_enabled(priv, node); if (ra_list->is_11n_enabled) @@ -451,7 +445,21 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter) int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter) { - return atomic_read(&adapter->bypass_tx_pending) ? false : true; + struct mwifiex_private *priv; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + continue; + if (!skb_queue_empty(&priv->bypass_txq)) + return false; + } + + return true; } /* @@ -465,9 +473,14 @@ mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter) for (i = 0; i < adapter->priv_num; ++i) { priv = adapter->priv[i]; - if (priv && !priv->port_open) + if (!priv) + continue; + if (!priv->port_open) continue; - if (priv && atomic_read(&priv->wmm.tx_pkts_queued)) + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + continue; + if (atomic_read(&priv->wmm.tx_pkts_queued)) return false; } @@ -737,7 +750,11 @@ mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr) if (!ra_list) continue; mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); - atomic_sub(ra_list->total_pkt_count, &priv->wmm.tx_pkts_queued); + if (ra_list->tx_paused) + priv->wmm.pkts_paused[i] -= ra_list->total_pkt_count; + else + atomic_sub(ra_list->total_pkt_count, + &priv->wmm.tx_pkts_queued); list_del(&ra_list->list); kfree(ra_list); } @@ -1086,6 +1103,10 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0)) continue; + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv_tmp)) + continue; + /* iterate over the WMM queues of the BSS */ hqp = &priv_tmp->wmm.highest_queued_prio; for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) { @@ -1321,8 +1342,7 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); if (adapter->iface_type == MWIFIEX_USB) { - adapter->data_sent = true; - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, skb, NULL); } else { tx_param.next_pkt_len = @@ -1351,15 +1371,11 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, ra_list_flags); break; case -1: - if (adapter->iface_type != MWIFIEX_PCIE) - adapter->data_sent = false; mwifiex_dbg(adapter, ERROR, "host_to_card failed: %#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); @@ -1467,6 +1483,13 @@ void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter) for (i = 0; i < adapter->priv_num; ++i) { priv = adapter->priv[i]; + if (!priv) + continue; + + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + continue; + if (skb_queue_empty(&priv->bypass_txq)) continue; diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 9420fc61c2e6..30e3aaae32e2 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -5423,7 +5423,7 @@ static int mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { int i, rc = 0; diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c index a9e94b6db5b7..0f6ea316e38e 100644 --- a/drivers/net/wireless/orinoco/cfg.c +++ b/drivers/net/wireless/orinoco/cfg.c @@ -220,7 +220,7 @@ static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed) if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { /* Set fragmentation */ if (priv->has_mwo) { - if (wiphy->frag_threshold < 0) + if (wiphy->frag_threshold == -1) frag_value = 0; else { printk(KERN_WARNING "%s: Fixed fragmentation " @@ -230,7 +230,7 @@ static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed) frag_value = 1; } } else { - if (wiphy->frag_threshold < 0) + if (wiphy->frag_threshold == -1) frag_value = 2346; else if ((wiphy->frag_threshold < 257) || (wiphy->frag_threshold > 2347)) @@ -252,7 +252,7 @@ static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed) * the upper limit. */ - if (wiphy->rts_threshold < 0) + if (wiphy->rts_threshold == -1) rts_value = 2347; else if (wiphy->rts_threshold > 2347) err = -EINVAL; diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 71a825c750cf..a13d1f2b5912 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -1236,7 +1236,7 @@ static int set_rts_threshold(struct usbnet *usbdev, u32 rts_threshold) netdev_dbg(usbdev->net, "%s(): %i\n", __func__, rts_threshold); - if (rts_threshold < 0 || rts_threshold > 2347) + if (rts_threshold == -1 || rts_threshold > 2347) rts_threshold = 2347; tmp = cpu_to_le32(rts_threshold); diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 7e804324bfa7..b5bcc933a2a6 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -664,6 +664,7 @@ static int rsi_mac80211_set_key(struct ieee80211_hw *hw, * @tid: Traffic identifier. * @ssn: Pointer to ssn value. * @buf_size: Buffer size (for kernel version > 2.6.38). + * @amsdu: is AMSDU in AMPDU allowed * * Return: status: 0 on success, negative error code on failure. */ @@ -673,7 +674,8 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_sta *sta, unsigned short tid, unsigned short *ssn, - unsigned char buf_size) + unsigned char buf_size, + bool amsdu) { int status = -EOPNOTSUPP; struct rsi_hw *adapter = hw->priv; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 9524564f873b..9733b31a780d 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -7937,7 +7937,7 @@ EXPORT_SYMBOL_GPL(rt2800_get_tsf); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv; int ret = 0; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index 1609b8a7f7eb..440790b92b19 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -220,7 +220,7 @@ u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size); + u8 buf_size, bool amsdu); int rt2800_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey); void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev); diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index 48a2cad29477..7e8bb1198ae9 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -266,7 +266,7 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, if (beacon_diff > beacon_int) beacon_diff = 0; - autowake_timeout = (conf->max_sleep_period * beacon_int) - beacon_diff; + autowake_timeout = (conf->ps_dtim_period * beacon_int) - beacon_diff; queue_delayed_work(rt2x00dev->workqueue, &rt2x00dev->autowakeup_work, autowake_timeout - 15); diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 585d0883c7e5..c925a4dff599 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -1373,7 +1373,7 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c index 7c355fff2c5e..ebed13af9852 100644 --- a/drivers/net/wireless/ti/wl12xx/scan.c +++ b/drivers/net/wireless/ti/wl12xx/scan.c @@ -350,7 +350,8 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, cfg->bss_type = SCAN_BSS_TYPE_ANY; /* currently NL80211 supports only a single interval */ for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) - cfg->intervals[i] = cpu_to_le32(req->interval); + cfg->intervals[i] = cpu_to_le32(req->scan_plans[0].interval * + MSEC_PER_SEC); cfg->ssid_len = 0; ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req); diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index abbf054fb6da..50cce42089a5 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -2115,3 +2115,4 @@ MODULE_PARM_DESC(num_rx_desc_param, MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_FIRMWARE(WL18XX_FW_NAME); +MODULE_FIRMWARE(WL18XX_CONF_FILE_NAME); diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c index c938c494c785..bc15aa2c3efa 100644 --- a/drivers/net/wireless/ti/wl18xx/scan.c +++ b/drivers/net/wireless/ti/wl18xx/scan.c @@ -228,13 +228,15 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl, wl18xx_adjust_channels(cmd, cmd_channels); if (c->num_short_intervals && c->long_interval && - c->long_interval > req->interval) { - cmd->short_cycles_msec = cpu_to_le16(req->interval); + c->long_interval > req->scan_plans[0].interval * MSEC_PER_SEC) { + cmd->short_cycles_msec = + cpu_to_le16(req->scan_plans[0].interval * MSEC_PER_SEC); cmd->long_cycles_msec = cpu_to_le16(c->long_interval); cmd->short_cycles_count = c->num_short_intervals; } else { cmd->short_cycles_msec = 0; - cmd->long_cycles_msec = cpu_to_le16(req->interval); + cmd->long_cycles_msec = + cpu_to_le16(req->scan_plans[0].interval * MSEC_PER_SEC); cmd->short_cycles_count = 0; } wl1271_debug(DEBUG_SCAN, "short_interval: %d, long_interval: %d, num_short: %d", diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index e819369d8f8f..ec7f6af3fab2 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5263,7 +5263,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) + u8 buf_size, bool amsdu) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index a1b6040e6491..906be6aa4eb6 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -318,7 +318,7 @@ struct wl1271 { bool watchdog_recovery; /* Reg domain last configuration */ - u32 reg_ch_conf_last[2]; + u32 reg_ch_conf_last[2] __aligned(8); /* Reg domain pending configuration */ u32 reg_ch_conf_pending[2]; diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index f821a97d7827..9bf63c27a9b7 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1819,19 +1819,22 @@ again: goto destroy_ring; } - if (num_queues == 1) { - err = write_queue_xenstore_keys(&info->queues[0], &xbt, 0); /* flat */ - if (err) - goto abort_transaction_no_dev_fatal; - } else { + if (xenbus_exists(XBT_NIL, + info->xbdev->otherend, "multi-queue-max-queues")) { /* Write the number of queues */ - err = xenbus_printf(xbt, dev->nodename, "multi-queue-num-queues", - "%u", num_queues); + err = xenbus_printf(xbt, dev->nodename, + "multi-queue-num-queues", "%u", num_queues); if (err) { message = "writing multi-queue-num-queues"; goto abort_transaction_no_dev_fatal; } + } + if (num_queues == 1) { + err = write_queue_xenstore_keys(&info->queues[0], &xbt, 0); /* flat */ + if (err) + goto abort_transaction_no_dev_fatal; + } else { /* Write the keys for each queue */ for (i = 0; i < num_queues; ++i) { queue = &info->queues[i]; diff --git a/drivers/ntb/hw/intel/ntb_hw_intel.c b/drivers/ntb/hw/intel/ntb_hw_intel.c index 87751cfd6f4f..865a3e3cc581 100644 --- a/drivers/ntb/hw/intel/ntb_hw_intel.c +++ b/drivers/ntb/hw/intel/ntb_hw_intel.c @@ -190,14 +190,17 @@ static inline int pdev_is_xeon(struct pci_dev *pdev) case PCI_DEVICE_ID_INTEL_NTB_SS_SNB: case PCI_DEVICE_ID_INTEL_NTB_SS_IVT: case PCI_DEVICE_ID_INTEL_NTB_SS_HSX: + case PCI_DEVICE_ID_INTEL_NTB_SS_BDX: case PCI_DEVICE_ID_INTEL_NTB_PS_JSF: case PCI_DEVICE_ID_INTEL_NTB_PS_SNB: case PCI_DEVICE_ID_INTEL_NTB_PS_IVT: case PCI_DEVICE_ID_INTEL_NTB_PS_HSX: + case PCI_DEVICE_ID_INTEL_NTB_PS_BDX: case PCI_DEVICE_ID_INTEL_NTB_B2B_JSF: case PCI_DEVICE_ID_INTEL_NTB_B2B_SNB: case PCI_DEVICE_ID_INTEL_NTB_B2B_IVT: case PCI_DEVICE_ID_INTEL_NTB_B2B_HSX: + case PCI_DEVICE_ID_INTEL_NTB_B2B_BDX: return 1; } return 0; @@ -237,7 +240,7 @@ static inline int ndev_ignore_unsafe(struct intel_ntb_dev *ndev, static int ndev_mw_to_bar(struct intel_ntb_dev *ndev, int idx) { - if (idx < 0 || idx > ndev->mw_count) + if (idx < 0 || idx >= ndev->mw_count) return -EINVAL; return ndev->reg->mw_bar[idx]; } @@ -572,10 +575,13 @@ static ssize_t ndev_debugfs_read(struct file *filp, char __user *ubuf, "Connection Topology -\t%s\n", ntb_topo_string(ndev->ntb.topo)); - off += scnprintf(buf + off, buf_size - off, - "B2B Offset -\t\t%#lx\n", ndev->b2b_off); - off += scnprintf(buf + off, buf_size - off, - "B2B MW Idx -\t\t%d\n", ndev->b2b_idx); + if (ndev->b2b_idx != UINT_MAX) { + off += scnprintf(buf + off, buf_size - off, + "B2B MW Idx -\t\t%u\n", ndev->b2b_idx); + off += scnprintf(buf + off, buf_size - off, + "B2B Offset -\t\t%#lx\n", ndev->b2b_off); + } + off += scnprintf(buf + off, buf_size - off, "BAR4 Split -\t\t%s\n", ndev->bar4_split ? "yes" : "no"); @@ -1484,7 +1490,7 @@ static int xeon_setup_b2b_mw(struct intel_ntb_dev *ndev, pdev = ndev_pdev(ndev); mmio = ndev->self_mmio; - if (ndev->b2b_idx >= ndev->mw_count) { + if (ndev->b2b_idx == UINT_MAX) { dev_dbg(ndev_dev(ndev), "not using b2b mw\n"); b2b_bar = 0; ndev->b2b_off = 0; @@ -1776,6 +1782,13 @@ static int xeon_init_ntb(struct intel_ntb_dev *ndev) else ndev->b2b_idx = b2b_mw_idx; + if (ndev->b2b_idx >= ndev->mw_count) { + dev_dbg(ndev_dev(ndev), + "b2b_mw_idx %d invalid for mw_count %u\n", + b2b_mw_idx, ndev->mw_count); + return -EINVAL; + } + dev_dbg(ndev_dev(ndev), "setting up b2b mw idx %d means %d\n", b2b_mw_idx, ndev->b2b_idx); @@ -1843,6 +1856,9 @@ static int xeon_init_dev(struct intel_ntb_dev *ndev) case PCI_DEVICE_ID_INTEL_NTB_SS_HSX: case PCI_DEVICE_ID_INTEL_NTB_PS_HSX: case PCI_DEVICE_ID_INTEL_NTB_B2B_HSX: + case PCI_DEVICE_ID_INTEL_NTB_SS_BDX: + case PCI_DEVICE_ID_INTEL_NTB_PS_BDX: + case PCI_DEVICE_ID_INTEL_NTB_B2B_BDX: ndev->hwerr_flags |= NTB_HWERR_SDOORBELL_LOCKUP; break; } @@ -1857,6 +1873,9 @@ static int xeon_init_dev(struct intel_ntb_dev *ndev) case PCI_DEVICE_ID_INTEL_NTB_SS_HSX: case PCI_DEVICE_ID_INTEL_NTB_PS_HSX: case PCI_DEVICE_ID_INTEL_NTB_B2B_HSX: + case PCI_DEVICE_ID_INTEL_NTB_SS_BDX: + case PCI_DEVICE_ID_INTEL_NTB_PS_BDX: + case PCI_DEVICE_ID_INTEL_NTB_B2B_BDX: ndev->hwerr_flags |= NTB_HWERR_SB01BASE_LOCKUP; break; } @@ -1878,6 +1897,9 @@ static int xeon_init_dev(struct intel_ntb_dev *ndev) case PCI_DEVICE_ID_INTEL_NTB_SS_HSX: case PCI_DEVICE_ID_INTEL_NTB_PS_HSX: case PCI_DEVICE_ID_INTEL_NTB_B2B_HSX: + case PCI_DEVICE_ID_INTEL_NTB_SS_BDX: + case PCI_DEVICE_ID_INTEL_NTB_PS_BDX: + case PCI_DEVICE_ID_INTEL_NTB_B2B_BDX: ndev->hwerr_flags |= NTB_HWERR_B2BDOORBELL_BIT14; break; } @@ -1996,7 +2018,7 @@ static inline void ndev_init_struct(struct intel_ntb_dev *ndev, ndev->ntb.ops = &intel_ntb_ops; ndev->b2b_off = 0; - ndev->b2b_idx = INT_MAX; + ndev->b2b_idx = UINT_MAX; ndev->bar4_split = 0; @@ -2234,14 +2256,17 @@ static const struct pci_device_id intel_ntb_pci_tbl[] = { {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_SNB)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_IVT)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_HSX)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_BDX)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_PS_JSF)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_PS_SNB)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_PS_IVT)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_PS_HSX)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_PS_BDX)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_SS_JSF)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_SS_SNB)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_SS_IVT)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_SS_HSX)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_SS_BDX)}, {0} }; MODULE_DEVICE_TABLE(pci, intel_ntb_pci_tbl); diff --git a/drivers/ntb/hw/intel/ntb_hw_intel.h b/drivers/ntb/hw/intel/ntb_hw_intel.h index 7ddaf387b679..ea0612f797df 100644 --- a/drivers/ntb/hw/intel/ntb_hw_intel.h +++ b/drivers/ntb/hw/intel/ntb_hw_intel.h @@ -67,6 +67,9 @@ #define PCI_DEVICE_ID_INTEL_NTB_PS_HSX 0x2F0E #define PCI_DEVICE_ID_INTEL_NTB_SS_HSX 0x2F0F #define PCI_DEVICE_ID_INTEL_NTB_B2B_BWD 0x0C4E +#define PCI_DEVICE_ID_INTEL_NTB_B2B_BDX 0x6F0D +#define PCI_DEVICE_ID_INTEL_NTB_PS_BDX 0x6F0E +#define PCI_DEVICE_ID_INTEL_NTB_SS_BDX 0x6F0F /* Intel Xeon hardware */ diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 1c6386d5f79c..6e3ee907d186 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -119,7 +119,8 @@ struct ntb_transport_qp { struct ntb_transport_ctx *transport; struct ntb_dev *ndev; void *cb_data; - struct dma_chan *dma_chan; + struct dma_chan *tx_dma_chan; + struct dma_chan *rx_dma_chan; bool client_ready; bool link_is_up; @@ -297,7 +298,7 @@ static LIST_HEAD(ntb_transport_list); static int ntb_bus_init(struct ntb_transport_ctx *nt) { - list_add(&nt->entry, &ntb_transport_list); + list_add_tail(&nt->entry, &ntb_transport_list); return 0; } @@ -452,7 +453,7 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count, out_offset = 0; out_offset += snprintf(buf + out_offset, out_count - out_offset, - "NTB QP stats\n"); + "\nNTB QP stats:\n\n"); out_offset += snprintf(buf + out_offset, out_count - out_offset, "rx_bytes - \t%llu\n", qp->rx_bytes); out_offset += snprintf(buf + out_offset, out_count - out_offset, @@ -470,11 +471,11 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count, out_offset += snprintf(buf + out_offset, out_count - out_offset, "rx_err_ver - \t%llu\n", qp->rx_err_ver); out_offset += snprintf(buf + out_offset, out_count - out_offset, - "rx_buff - \t%p\n", qp->rx_buff); + "rx_buff - \t0x%p\n", qp->rx_buff); out_offset += snprintf(buf + out_offset, out_count - out_offset, "rx_index - \t%u\n", qp->rx_index); out_offset += snprintf(buf + out_offset, out_count - out_offset, - "rx_max_entry - \t%u\n", qp->rx_max_entry); + "rx_max_entry - \t%u\n\n", qp->rx_max_entry); out_offset += snprintf(buf + out_offset, out_count - out_offset, "tx_bytes - \t%llu\n", qp->tx_bytes); @@ -489,15 +490,32 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count, out_offset += snprintf(buf + out_offset, out_count - out_offset, "tx_err_no_buf - %llu\n", qp->tx_err_no_buf); out_offset += snprintf(buf + out_offset, out_count - out_offset, - "tx_mw - \t%p\n", qp->tx_mw); + "tx_mw - \t0x%p\n", qp->tx_mw); out_offset += snprintf(buf + out_offset, out_count - out_offset, - "tx_index - \t%u\n", qp->tx_index); + "tx_index (H) - \t%u\n", qp->tx_index); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "RRI (T) - \t%u\n", + qp->remote_rx_info->entry); out_offset += snprintf(buf + out_offset, out_count - out_offset, "tx_max_entry - \t%u\n", qp->tx_max_entry); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "free tx - \t%u\n", + ntb_transport_tx_free_entry(qp)); out_offset += snprintf(buf + out_offset, out_count - out_offset, - "\nQP Link %s\n", + "\n"); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Using TX DMA - \t%s\n", + qp->tx_dma_chan ? "Yes" : "No"); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Using RX DMA - \t%s\n", + qp->rx_dma_chan ? "Yes" : "No"); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "QP Link - \t%s\n", qp->link_is_up ? "Up" : "Down"); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "\n"); + if (out_offset > out_count) out_offset = out_count; @@ -535,6 +553,7 @@ static struct ntb_queue_entry *ntb_list_rm(spinlock_t *lock, } entry = list_first_entry(list, struct ntb_queue_entry, entry); list_del(&entry->entry); + out: spin_unlock_irqrestore(lock, flags); @@ -1206,7 +1225,7 @@ static void ntb_async_rx(struct ntb_queue_entry *entry, void *offset) { struct dma_async_tx_descriptor *txd; struct ntb_transport_qp *qp = entry->qp; - struct dma_chan *chan = qp->dma_chan; + struct dma_chan *chan = qp->rx_dma_chan; struct dma_device *device; size_t pay_off, buff_off, len; struct dmaengine_unmap_data *unmap; @@ -1219,18 +1238,18 @@ static void ntb_async_rx(struct ntb_queue_entry *entry, void *offset) goto err; if (len < copy_bytes) - goto err_wait; + goto err; device = chan->device; pay_off = (size_t)offset & ~PAGE_MASK; buff_off = (size_t)buf & ~PAGE_MASK; if (!is_dma_copy_aligned(device, pay_off, buff_off, len)) - goto err_wait; + goto err; unmap = dmaengine_get_unmap_data(device->dev, 2, GFP_NOWAIT); if (!unmap) - goto err_wait; + goto err; unmap->len = len; unmap->addr[0] = dma_map_page(device->dev, virt_to_page(offset), @@ -1273,12 +1292,6 @@ err_set_unmap: dmaengine_unmap_put(unmap); err_get_unmap: dmaengine_unmap_put(unmap); -err_wait: - /* If the callbacks come out of order, the writing of the index to the - * last completed will be out of order. This may result in the - * receive stalling forever. - */ - dma_sync_wait(chan, qp->last_cookie); err: ntb_memcpy_rx(entry, offset); qp->rx_memcpy++; @@ -1373,8 +1386,8 @@ static void ntb_transport_rxc_db(unsigned long data) break; } - if (i && qp->dma_chan) - dma_async_issue_pending(qp->dma_chan); + if (i && qp->rx_dma_chan) + dma_async_issue_pending(qp->rx_dma_chan); if (i == qp->rx_max_entry) { /* there is more work to do */ @@ -1441,7 +1454,7 @@ static void ntb_async_tx(struct ntb_transport_qp *qp, { struct ntb_payload_header __iomem *hdr; struct dma_async_tx_descriptor *txd; - struct dma_chan *chan = qp->dma_chan; + struct dma_chan *chan = qp->tx_dma_chan; struct dma_device *device; size_t dest_off, buff_off; struct dmaengine_unmap_data *unmap; @@ -1634,14 +1647,27 @@ ntb_transport_create_queue(void *data, struct device *client_dev, dma_cap_set(DMA_MEMCPY, dma_mask); if (use_dma) { - qp->dma_chan = dma_request_channel(dma_mask, ntb_dma_filter_fn, - (void *)(unsigned long)node); - if (!qp->dma_chan) - dev_info(&pdev->dev, "Unable to allocate DMA channel\n"); + qp->tx_dma_chan = + dma_request_channel(dma_mask, ntb_dma_filter_fn, + (void *)(unsigned long)node); + if (!qp->tx_dma_chan) + dev_info(&pdev->dev, "Unable to allocate TX DMA channel\n"); + + qp->rx_dma_chan = + dma_request_channel(dma_mask, ntb_dma_filter_fn, + (void *)(unsigned long)node); + if (!qp->rx_dma_chan) + dev_info(&pdev->dev, "Unable to allocate RX DMA channel\n"); } else { - qp->dma_chan = NULL; + qp->tx_dma_chan = NULL; + qp->rx_dma_chan = NULL; } - dev_dbg(&pdev->dev, "Using %s memcpy\n", qp->dma_chan ? "DMA" : "CPU"); + + dev_dbg(&pdev->dev, "Using %s memcpy for TX\n", + qp->tx_dma_chan ? "DMA" : "CPU"); + + dev_dbg(&pdev->dev, "Using %s memcpy for RX\n", + qp->rx_dma_chan ? "DMA" : "CPU"); for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) { entry = kzalloc_node(sizeof(*entry), GFP_ATOMIC, node); @@ -1676,8 +1702,10 @@ err2: err1: while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q))) kfree(entry); - if (qp->dma_chan) - dma_release_channel(qp->dma_chan); + if (qp->tx_dma_chan) + dma_release_channel(qp->tx_dma_chan); + if (qp->rx_dma_chan) + dma_release_channel(qp->rx_dma_chan); nt->qp_bitmap_free |= qp_bit; err: return NULL; @@ -1701,12 +1729,27 @@ void ntb_transport_free_queue(struct ntb_transport_qp *qp) pdev = qp->ndev->pdev; - if (qp->dma_chan) { - struct dma_chan *chan = qp->dma_chan; + if (qp->tx_dma_chan) { + struct dma_chan *chan = qp->tx_dma_chan; + /* Putting the dma_chan to NULL will force any new traffic to be + * processed by the CPU instead of the DAM engine + */ + qp->tx_dma_chan = NULL; + + /* Try to be nice and wait for any queued DMA engine + * transactions to process before smashing it with a rock + */ + dma_sync_wait(chan, qp->last_cookie); + dmaengine_terminate_all(chan); + dma_release_channel(chan); + } + + if (qp->rx_dma_chan) { + struct dma_chan *chan = qp->rx_dma_chan; /* Putting the dma_chan to NULL will force any new traffic to be * processed by the CPU instead of the DAM engine */ - qp->dma_chan = NULL; + qp->rx_dma_chan = NULL; /* Try to be nice and wait for any queued DMA engine * transactions to process before smashing it with a rock @@ -1843,7 +1886,7 @@ int ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data, entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q); if (!entry) { qp->tx_err_no_buf++; - return -ENOMEM; + return -EBUSY; } entry->cb_data = cb; @@ -1954,21 +1997,34 @@ EXPORT_SYMBOL_GPL(ntb_transport_qp_num); unsigned int ntb_transport_max_size(struct ntb_transport_qp *qp) { unsigned int max; + unsigned int copy_align; if (!qp) return 0; - if (!qp->dma_chan) + if (!qp->tx_dma_chan && !qp->rx_dma_chan) return qp->tx_max_frame - sizeof(struct ntb_payload_header); + copy_align = max(qp->tx_dma_chan->device->copy_align, + qp->rx_dma_chan->device->copy_align); + /* If DMA engine usage is possible, try to find the max size for that */ max = qp->tx_max_frame - sizeof(struct ntb_payload_header); - max -= max % (1 << qp->dma_chan->device->copy_align); + max -= max % (1 << copy_align); return max; } EXPORT_SYMBOL_GPL(ntb_transport_max_size); +unsigned int ntb_transport_tx_free_entry(struct ntb_transport_qp *qp) +{ + unsigned int head = qp->tx_index; + unsigned int tail = qp->remote_rx_info->entry; + + return tail > head ? tail - head : qp->tx_max_entry + tail - head; +} +EXPORT_SYMBOL_GPL(ntb_transport_tx_free_entry); + static void ntb_transport_doorbell_callback(void *data, int vector) { struct ntb_transport_ctx *nt = data; diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c index 59ad54a63d9f..cb477518dd0e 100644 --- a/drivers/nvdimm/btt_devs.c +++ b/drivers/nvdimm/btt_devs.c @@ -128,13 +128,13 @@ static ssize_t namespace_store(struct device *dev, struct nd_btt *nd_btt = to_nd_btt(dev); ssize_t rc; - nvdimm_bus_lock(dev); device_lock(dev); + nvdimm_bus_lock(dev); rc = nd_namespace_store(dev, &nd_btt->ndns, buf, len); dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, rc, buf, buf[len - 1] == '\n' ? "" : "\n"); - device_unlock(dev); nvdimm_bus_unlock(dev); + device_unlock(dev); return rc; } diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 3fd7d0d81a47..71805a1aa0f3 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -148,13 +148,13 @@ static ssize_t namespace_store(struct device *dev, struct nd_pfn *nd_pfn = to_nd_pfn(dev); ssize_t rc; - nvdimm_bus_lock(dev); device_lock(dev); + nvdimm_bus_lock(dev); rc = nd_namespace_store(dev, &nd_pfn->ndns, buf, len); dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, rc, buf, buf[len - 1] == '\n' ? "" : "\n"); - device_unlock(dev); nvdimm_bus_unlock(dev); + device_unlock(dev); return rc; } diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index b9525385c0dc..0ba6a978f227 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -92,6 +92,8 @@ static int pmem_rw_page(struct block_device *bdev, sector_t sector, struct pmem_device *pmem = bdev->bd_disk->private_data; pmem_do_bvec(pmem, page, PAGE_CACHE_SIZE, 0, rw, sector); + if (rw & WRITE) + wmb_pmem(); page_endio(page, rw & WRITE, 0); return 0; diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 1350fa25cdb0..a87a868fed64 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -197,7 +197,8 @@ static int of_phy_match(struct device *dev, void *phy_np) * of_phy_find_device - Give a PHY node, find the phy_device * @phy_np: Pointer to the phy's device tree node * - * Returns a pointer to the phy_device. + * If successful, returns a pointer to the phy_device with the embedded + * struct device refcount incremented by one, or NULL on failure. */ struct phy_device *of_phy_find_device(struct device_node *phy_np) { @@ -217,7 +218,9 @@ EXPORT_SYMBOL(of_phy_find_device); * @hndlr: Link state callback for the network device * @iface: PHY data interface type * - * Returns a pointer to the phy_device if successful. NULL otherwise + * If successful, returns a pointer to the phy_device with the embedded + * struct device refcount incremented by one, or NULL on failure. The + * refcount must be dropped by calling phy_disconnect() or phy_detach(). */ struct phy_device *of_phy_connect(struct net_device *dev, struct device_node *phy_np, @@ -225,13 +228,19 @@ struct phy_device *of_phy_connect(struct net_device *dev, phy_interface_t iface) { struct phy_device *phy = of_phy_find_device(phy_np); + int ret; if (!phy) return NULL; phy->dev_flags = flags; - return phy_connect_direct(dev, phy, hndlr, iface) ? NULL : phy; + ret = phy_connect_direct(dev, phy, hndlr, iface); + + /* refcount is held by phy_connect_direct() on success */ + put_device(&phy->dev); + + return ret ? NULL : phy; } EXPORT_SYMBOL(of_phy_connect); @@ -241,17 +250,27 @@ EXPORT_SYMBOL(of_phy_connect); * @phy_np: Node pointer for the PHY * @flags: flags to pass to the PHY * @iface: PHY data interface type + * + * If successful, returns a pointer to the phy_device with the embedded + * struct device refcount incremented by one, or NULL on failure. The + * refcount must be dropped by calling phy_disconnect() or phy_detach(). */ struct phy_device *of_phy_attach(struct net_device *dev, struct device_node *phy_np, u32 flags, phy_interface_t iface) { struct phy_device *phy = of_phy_find_device(phy_np); + int ret; if (!phy) return NULL; - return phy_attach_direct(dev, phy, flags, iface) ? NULL : phy; + ret = phy_attach_direct(dev, phy, flags, iface); + + /* refcount is held by phy_attach_direct() on success */ + put_device(&phy->dev); + + return ret ? NULL : phy; } EXPORT_SYMBOL(of_phy_attach); diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c index 1710d9dc7fc2..2306313c0029 100644 --- a/drivers/of/of_pci_irq.c +++ b/drivers/of/of_pci_irq.c @@ -38,8 +38,8 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq */ rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); if (rc != 0) - return rc; - /* No pin, exit */ + goto err; + /* No pin, exit with no error message. */ if (pin == 0) return -ENODEV; @@ -53,8 +53,10 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq ppnode = pci_bus_to_OF_node(pdev->bus); /* No node for host bridge ? give up */ - if (ppnode == NULL) - return -EINVAL; + if (ppnode == NULL) { + rc = -EINVAL; + goto err; + } } else { /* We found a P2P bridge, check if it has a node */ ppnode = pci_device_to_OF_node(ppdev); @@ -86,7 +88,13 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq out_irq->args[0] = pin; laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); laddr[1] = laddr[2] = cpu_to_be32(0); - return of_irq_parse_raw(laddr, out_irq); + rc = of_irq_parse_raw(laddr, out_irq); + if (rc) + goto err; + return 0; +err: + dev_err(&pdev->dev, "of_irq_parse_pci() failed with rc=%d\n", rc); + return rc; } EXPORT_SYMBOL_GPL(of_irq_parse_pci); @@ -105,10 +113,8 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin) int ret; ret = of_irq_parse_pci(dev, &oirq); - if (ret) { - dev_err(&dev->dev, "of_irq_parse_pci() failed with rc=%d\n", ret); + if (ret) return 0; /* Proper return code 0 == NO_IRQ */ - } return irq_create_of_mapping(&oirq); } diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c index baec33c4e698..a0580afe1713 100644 --- a/drivers/parisc/dino.c +++ b/drivers/parisc/dino.c @@ -560,6 +560,9 @@ dino_fixup_bus(struct pci_bus *bus) } else if (bus->parent) { int i; + pci_read_bridge_bases(bus); + + for(i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { if((bus->self->resource[i].flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index 7b9e89ba0465..a32c1f6c252c 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -693,6 +693,7 @@ lba_fixup_bus(struct pci_bus *bus) if (bus->parent) { int i; /* PCI-PCI Bridge */ + pci_read_bridge_bases(bus); for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) pci_claim_bridge_resource(bus->self, i); } else { diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 769f7e35f1a2..59ac36fe7c42 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -442,7 +442,8 @@ static const struct pci_vpd_ops pci_vpd_pci22_ops = { static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, void *arg) { - struct pci_dev *tdev = pci_get_slot(dev->bus, PCI_SLOT(dev->devfn)); + struct pci_dev *tdev = pci_get_slot(dev->bus, + PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); ssize_t ret; if (!tdev) @@ -456,7 +457,8 @@ static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count, const void *arg) { - struct pci_dev *tdev = pci_get_slot(dev->bus, PCI_SLOT(dev->devfn)); + struct pci_dev *tdev = pci_get_slot(dev->bus, + PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); ssize_t ret; if (!tdev) @@ -473,22 +475,6 @@ static const struct pci_vpd_ops pci_vpd_f0_ops = { .release = pci_vpd_pci22_release, }; -static int pci_vpd_f0_dev_check(struct pci_dev *dev) -{ - struct pci_dev *tdev = pci_get_slot(dev->bus, PCI_SLOT(dev->devfn)); - int ret = 0; - - if (!tdev) - return -ENODEV; - if (!tdev->vpd || !tdev->multifunction || - dev->class != tdev->class || dev->vendor != tdev->vendor || - dev->device != tdev->device) - ret = -ENODEV; - - pci_dev_put(tdev); - return ret; -} - int pci_vpd_pci22_init(struct pci_dev *dev) { struct pci_vpd_pci22 *vpd; @@ -497,12 +483,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev) cap = pci_find_capability(dev, PCI_CAP_ID_VPD); if (!cap) return -ENODEV; - if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) { - int ret = pci_vpd_f0_dev_check(dev); - if (ret) - return ret; - } vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC); if (!vpd) return -ENOMEM; diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 6fbd3f2b5992..d3346d23963b 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -256,6 +256,8 @@ bool pci_bus_clip_resource(struct pci_dev *dev, int idx) res->start = start; res->end = end; + res->flags &= ~IORESOURCE_UNSET; + orig_res.flags &= ~IORESOURCE_UNSET; dev_printk(KERN_DEBUG, &dev->dev, "%pR clipped to %pR\n", &orig_res, res); diff --git a/drivers/pci/host/pci-keystone.c b/drivers/pci/host/pci-keystone.c index 81253e70b1c5..0aa81bd3de12 100644 --- a/drivers/pci/host/pci-keystone.c +++ b/drivers/pci/host/pci-keystone.c @@ -110,7 +110,7 @@ static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie) return -EINVAL; } -static void ks_pcie_msi_irq_handler(unsigned int __irq, struct irq_desc *desc) +static void ks_pcie_msi_irq_handler(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); @@ -138,8 +138,7 @@ static void ks_pcie_msi_irq_handler(unsigned int __irq, struct irq_desc *desc) * Traverse through pending legacy interrupts and invoke handler for each. Also * takes care of interrupt controller level mask/ack operation. */ -static void ks_pcie_legacy_irq_handler(unsigned int __irq, - struct irq_desc *desc) +static void ks_pcie_legacy_irq_handler(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index 367e28fa7564..c4f64bfee551 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -362,6 +362,7 @@ static int rcar_pci_probe(struct platform_device *pdev) static struct of_device_id rcar_pci_of_match[] = { { .compatible = "renesas,pci-r8a7790", }, { .compatible = "renesas,pci-r8a7791", }, + { .compatible = "renesas,pci-r8a7794", }, { }, }; diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c index 996327cfa1e1..e491681daf22 100644 --- a/drivers/pci/host/pci-xgene-msi.c +++ b/drivers/pci/host/pci-xgene-msi.c @@ -295,7 +295,7 @@ static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi) return 0; } -static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc) +static void xgene_msi_isr(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct xgene_msi_group *msi_groups; diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index dd652f2ae03d..108a3118ace7 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -299,9 +299,10 @@ static long local_pci_probe(void *_ddi) * Unbound PCI devices are always put in D0, regardless of * runtime PM status. During probe, the device is set to * active and the usage count is incremented. If the driver - * supports runtime PM, it should call pm_runtime_put_noidle() - * in its probe routine and pm_runtime_get_noresume() in its - * remove routine. + * supports runtime PM, it should call pm_runtime_put_noidle(), + * or any other runtime PM helper function decrementing the usage + * count, in its probe routine and pm_runtime_get_noresume() in + * its remove routine. */ pm_runtime_get_sync(dev); pci_dev->driver = pci_drv; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0b2be174d981..8361d27e5eca 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -676,15 +676,20 @@ static struct irq_domain *pci_host_bridge_msi_domain(struct pci_bus *bus) static void pci_set_bus_msi_domain(struct pci_bus *bus) { struct irq_domain *d; + struct pci_bus *b; /* - * Either bus is the root, and we must obtain it from the - * firmware, or we inherit it from the bridge device. + * The bus can be a root bus, a subordinate bus, or a virtual bus + * created by an SR-IOV device. Walk up to the first bridge device + * found or derive the domain from the host bridge. */ - if (pci_is_root_bus(bus)) - d = pci_host_bridge_msi_domain(bus); - else - d = dev_get_msi_domain(&bus->self->dev); + for (b = bus, d = NULL; !d && !pci_is_root_bus(b); b = b->parent) { + if (b->self) + d = dev_get_msi_domain(&b->self->dev); + } + + if (!d) + d = pci_host_bridge_msi_domain(b); dev_set_msi_domain(&bus->dev, d); } @@ -855,9 +860,6 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) child->bridge_ctl = bctl; } - /* Read and initialize bridge resources */ - pci_read_bridge_bases(child); - cmax = pci_scan_child_bus(child); if (cmax > subordinate) dev_warn(&dev->dev, "bridge has subordinate %02x but max busn %02x\n", @@ -918,9 +920,6 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) if (!is_cardbus) { child->bridge_ctl = bctl; - - /* Read and initialize bridge resources */ - pci_read_bridge_bases(child); max = pci_scan_child_bus(child); } else { /* diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 6a30252cd79f..b03373fd05ca 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1907,11 +1907,27 @@ static void quirk_netmos(struct pci_dev *dev) DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, PCI_CLASS_COMMUNICATION_SERIAL, 8, quirk_netmos); +/* + * Quirk non-zero PCI functions to route VPD access through function 0 for + * devices that share VPD resources between functions. The functions are + * expected to be identical devices. + */ static void quirk_f0_vpd_link(struct pci_dev *dev) { - if (!dev->multifunction || !PCI_FUNC(dev->devfn)) + struct pci_dev *f0; + + if (!PCI_FUNC(dev->devfn)) return; - dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0; + + f0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + if (!f0) + return; + + if (f0->vpd && dev->class == f0->class && + dev->vendor == f0->vendor && dev->device == f0->device) + dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0; + + pci_dev_put(f0); } DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET, 8, quirk_f0_vpd_link); diff --git a/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c b/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c index 7d9482bf8252..1ca783098e47 100644 --- a/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c @@ -143,7 +143,7 @@ static inline bool cygnus_get_bit(struct cygnus_gpio *chip, unsigned int reg, return !!(readl(chip->base + offset) & BIT(shift)); } -static void cygnus_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +static void cygnus_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct cygnus_gpio *chip = to_cygnus_gpio(gc); diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 69723e07036b..9638a00c67c2 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -349,6 +349,9 @@ static bool pinctrl_ready_for_gpio_range(unsigned gpio) struct pinctrl_gpio_range *range = NULL; struct gpio_chip *chip = gpio_to_chip(gpio); + if (WARN(!chip, "no gpio_chip for gpio%i?", gpio)) + return false; + mutex_lock(&pinctrldev_list_mutex); /* Loop over the pin controllers */ diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index dac4865f3203..f79ea430f651 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -425,7 +425,7 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) } } -static void byt_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +static void byt_gpio_irq_handler(struct irq_desc *desc) { struct irq_data *data = irq_desc_get_irq_data(desc); struct byt_gpio *vg = to_byt_gpio(irq_desc_get_handler_data(desc)); diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 2d5d3ddc36e5..270c127e03ea 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1414,7 +1414,7 @@ static struct irq_chip chv_gpio_irqchip = { .flags = IRQCHIP_SKIP_SET_WAKE, }; -static void chv_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +static void chv_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct chv_pinctrl *pctrl = gpiochip_to_pinctrl(gc); diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index bb377c110541..54848b8decef 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -836,7 +836,7 @@ static void intel_gpio_community_irq_handler(struct gpio_chip *gc, } } -static void intel_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +static void intel_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct intel_pinctrl *pctrl = gpiochip_to_pinctrl(gc); diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c index 7726c6caaf83..1b22f96ba839 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c @@ -1190,7 +1190,7 @@ mtk_eint_debounce_process(struct mtk_pinctrl *pctl, int index) } } -static void mtk_eint_irq_handler(unsigned irq, struct irq_desc *desc) +static void mtk_eint_irq_handler(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct mtk_pinctrl *pctl = irq_desc_get_handler_data(desc); diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik.c b/drivers/pinctrl/nomadik/pinctrl-nomadik.c index 352ede13a9e9..96cf03908e93 100644 --- a/drivers/pinctrl/nomadik/pinctrl-nomadik.c +++ b/drivers/pinctrl/nomadik/pinctrl-nomadik.c @@ -860,7 +860,7 @@ static void __nmk_gpio_irq_handler(struct irq_desc *desc, u32 status) chained_irq_exit(host_chip, desc); } -static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +static void nmk_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *chip = irq_desc_get_handler_data(desc); struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); @@ -873,7 +873,7 @@ static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) __nmk_gpio_irq_handler(desc, status); } -static void nmk_gpio_latent_irq_handler(unsigned int irq, struct irq_desc *desc) +static void nmk_gpio_latent_irq_handler(struct irq_desc *desc) { struct gpio_chip *chip = irq_desc_get_handler_data(desc); struct nmk_gpio_chip *nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); diff --git a/drivers/pinctrl/pinctrl-adi2.c b/drivers/pinctrl/pinctrl-adi2.c index a5976ebc4482..f6be68518c87 100644 --- a/drivers/pinctrl/pinctrl-adi2.c +++ b/drivers/pinctrl/pinctrl-adi2.c @@ -530,8 +530,7 @@ static inline void preflow_handler(struct irq_desc *desc) static inline void preflow_handler(struct irq_desc *desc) { } #endif -static void adi_gpio_handle_pint_irq(unsigned int inta_irq, - struct irq_desc *desc) +static void adi_gpio_handle_pint_irq(struct irq_desc *desc) { u32 request; u32 level_mask, hwirq; diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 5e86bb8ca80e..3318f1d6193c 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -492,15 +492,15 @@ static struct irq_chip amd_gpio_irqchip = { .irq_set_type = amd_gpio_irq_set_type, }; -static void amd_gpio_irq_handler(unsigned int __irq, struct irq_desc *desc) +static void amd_gpio_irq_handler(struct irq_desc *desc) { - unsigned int irq = irq_desc_get_irq(desc); u32 i; u32 off; u32 reg; u32 pin_reg; u64 reg64; int handled = 0; + unsigned int irq; unsigned long flags; struct irq_chip *chip = irq_desc_get_chip(desc); struct gpio_chip *gc = irq_desc_get_handler_data(desc); @@ -541,7 +541,7 @@ static void amd_gpio_irq_handler(unsigned int __irq, struct irq_desc *desc) } if (handled == 0) - handle_bad_irq(irq, desc); + handle_bad_irq(desc); spin_lock_irqsave(&gpio_dev->lock, flags); reg = readl(gpio_dev->base + WAKE_INT_MASTER_REG); diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index bae0012ee356..b0fde0f385e6 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -1585,7 +1585,7 @@ static struct irq_chip gpio_irqchip = { .irq_set_wake = gpio_irq_set_wake, }; -static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) +static void gpio_irq_handler(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct gpio_chip *gpio_chip = irq_desc_get_handler_data(desc); diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index 3731cc67a88b..9c9b88934bcc 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -519,7 +519,7 @@ static struct irq_chip u300_gpio_irqchip = { .irq_set_type = u300_gpio_irq_type, }; -static void u300_gpio_irq_handler(unsigned __irq, struct irq_desc *desc) +static void u300_gpio_irq_handler(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct irq_chip *parent_chip = irq_desc_get_chip(desc); diff --git a/drivers/pinctrl/pinctrl-digicolor.c b/drivers/pinctrl/pinctrl-digicolor.c index 461fffc4c62a..11f8b835d3b6 100644 --- a/drivers/pinctrl/pinctrl-digicolor.c +++ b/drivers/pinctrl/pinctrl-digicolor.c @@ -337,9 +337,9 @@ static int dc_pinctrl_probe(struct platform_device *pdev) pmap->dev = &pdev->dev; pmap->pctl = pinctrl_register(pctl_desc, &pdev->dev, pmap); - if (!pmap->pctl) { + if (IS_ERR(pmap->pctl)) { dev_err(&pdev->dev, "pinctrl driver registration failed\n"); - return -EINVAL; + return PTR_ERR(pmap->pctl); } ret = dc_gpiochip_add(pmap, pdev->dev.of_node); diff --git a/drivers/pinctrl/pinctrl-pistachio.c b/drivers/pinctrl/pinctrl-pistachio.c index 3dc2ae15f3a1..952b1c623887 100644 --- a/drivers/pinctrl/pinctrl-pistachio.c +++ b/drivers/pinctrl/pinctrl-pistachio.c @@ -1303,20 +1303,18 @@ static int pistachio_gpio_irq_set_type(struct irq_data *data, unsigned int type) } if (type & IRQ_TYPE_LEVEL_MASK) - __irq_set_handler_locked(data->irq, handle_level_irq); + irq_set_handler_locked(data, handle_level_irq); else - __irq_set_handler_locked(data->irq, handle_edge_irq); + irq_set_handler_locked(data, handle_edge_irq); return 0; } -static void pistachio_gpio_irq_handler(unsigned int __irq, - struct irq_desc *desc) +static void pistachio_gpio_irq_handler(struct irq_desc *desc) { - unsigned int irq = irq_desc_get_irq(desc); struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct pistachio_gpio_bank *bank = gc_to_bank(gc); - struct irq_chip *chip = irq_get_chip(irq); + struct irq_chip *chip = irq_desc_get_chip(desc); unsigned long pending; unsigned int pin; diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index c5246c05f70c..88bb707e107a 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -1475,7 +1475,7 @@ static const struct gpio_chip rockchip_gpiolib_chip = { * Interrupt handling */ -static void rockchip_irq_demux(unsigned int __irq, struct irq_desc *desc) +static void rockchip_irq_demux(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct rockchip_pin_bank *bank = irq_desc_get_handler_data(desc); diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index bf548c2a7a9d..ef04b962c3d5 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -1679,7 +1679,7 @@ static irqreturn_t pcs_irq_handler(int irq, void *d) * Use this if you have a separate interrupt for each * pinctrl-single instance. */ -static void pcs_irq_chain_handler(unsigned int irq, struct irq_desc *desc) +static void pcs_irq_chain_handler(struct irq_desc *desc) { struct pcs_soc_data *pcs_soc = irq_desc_get_handler_data(desc); struct irq_chip *chip; diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index f8338d2e6b6b..389526e704fb 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -1460,7 +1460,7 @@ static void __gpio_irq_handler(struct st_gpio_bank *bank) } } -static void st_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +static void st_gpio_irq_handler(struct irq_desc *desc) { /* interrupt dedicated per bank */ struct irq_chip *chip = irq_desc_get_chip(desc); @@ -1472,7 +1472,7 @@ static void st_gpio_irq_handler(unsigned irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } -static void st_gpio_irqmux_handler(unsigned irq, struct irq_desc *desc) +static void st_gpio_irqmux_handler(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct st_pinctrl *info = irq_desc_get_handler_data(desc); diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 67e08cb315c4..29984b36926a 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -313,8 +313,7 @@ static int pinmux_func_name_to_selector(struct pinctrl_dev *pctldev, /* See if this pctldev has this function */ while (selector < nfuncs) { - const char *fname = ops->get_function_name(pctldev, - selector); + const char *fname = ops->get_function_name(pctldev, selector); if (!strcmp(function, fname)) return selector; diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 492cdd51dc5c..a0c7407c1cac 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -765,9 +765,8 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_set_wake = msm_gpio_irq_set_wake, }; -static void msm_gpio_irq_handler(unsigned int __irq, struct irq_desc *desc) +static void msm_gpio_irq_handler(struct irq_desc *desc) { - unsigned int irq = irq_desc_get_irq(desc); struct gpio_chip *gc = irq_desc_get_handler_data(desc); const struct msm_pingroup *g; struct msm_pinctrl *pctrl = to_msm_pinctrl(gc); @@ -795,7 +794,7 @@ static void msm_gpio_irq_handler(unsigned int __irq, struct irq_desc *desc) /* No interrupts were flagged */ if (handled == 0) - handle_bad_irq(irq, desc); + handle_bad_irq(desc); chained_irq_exit(chip, desc); } diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c index c978b311031b..e1a3721bc8e5 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c @@ -723,9 +723,9 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev) #endif pctrl->pctrl = pinctrl_register(&pctrl->desc, &pdev->dev, pctrl); - if (!pctrl->pctrl) { + if (IS_ERR(pctrl->pctrl)) { dev_err(&pdev->dev, "couldn't register pm8xxx gpio driver\n"); - return -ENODEV; + return PTR_ERR(pctrl->pctrl); } pctrl->chip = pm8xxx_gpio_template; diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c b/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c index 2d1b69f171be..6652b8d7f707 100644 --- a/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c @@ -814,9 +814,9 @@ static int pm8xxx_mpp_probe(struct platform_device *pdev) #endif pctrl->pctrl = pinctrl_register(&pctrl->desc, &pdev->dev, pctrl); - if (!pctrl->pctrl) { + if (IS_ERR(pctrl->pctrl)) { dev_err(&pdev->dev, "couldn't register pm8xxx mpp driver\n"); - return -ENODEV; + return PTR_ERR(pctrl->pctrl); } pctrl->chip = pm8xxx_mpp_template; diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c index 5f45caaef46d..71ccf6a90b22 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.c +++ b/drivers/pinctrl/samsung/pinctrl-exynos.c @@ -419,7 +419,7 @@ static const struct of_device_id exynos_wkup_irq_ids[] = { }; /* interrupt handler for wakeup interrupts 0..15 */ -static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc) +static void exynos_irq_eint0_15(struct irq_desc *desc) { struct exynos_weint_data *eintd = irq_desc_get_handler_data(desc); struct samsung_pin_bank *bank = eintd->bank; @@ -451,7 +451,7 @@ static inline void exynos_irq_demux_eint(unsigned long pend, } /* interrupt handler for wakeup interrupt 16 */ -static void exynos_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc) +static void exynos_irq_demux_eint16_31(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct exynos_muxed_weint_data *eintd = irq_desc_get_handler_data(desc); diff --git a/drivers/pinctrl/samsung/pinctrl-s3c24xx.c b/drivers/pinctrl/samsung/pinctrl-s3c24xx.c index 019844d479bb..3d92f827da7a 100644 --- a/drivers/pinctrl/samsung/pinctrl-s3c24xx.c +++ b/drivers/pinctrl/samsung/pinctrl-s3c24xx.c @@ -240,7 +240,7 @@ static struct irq_chip s3c2410_eint0_3_chip = { .irq_set_type = s3c24xx_eint_type, }; -static void s3c2410_demux_eint0_3(unsigned int irq, struct irq_desc *desc) +static void s3c2410_demux_eint0_3(struct irq_desc *desc) { struct irq_data *data = irq_desc_get_irq_data(desc); struct s3c24xx_eint_data *eint_data = irq_desc_get_handler_data(desc); @@ -295,7 +295,7 @@ static struct irq_chip s3c2412_eint0_3_chip = { .irq_set_type = s3c24xx_eint_type, }; -static void s3c2412_demux_eint0_3(unsigned int irq, struct irq_desc *desc) +static void s3c2412_demux_eint0_3(struct irq_desc *desc) { struct s3c24xx_eint_data *eint_data = irq_desc_get_handler_data(desc); struct irq_data *data = irq_desc_get_irq_data(desc); @@ -361,7 +361,7 @@ static inline void s3c24xx_demux_eint(struct irq_desc *desc, u32 offset, u32 range) { struct s3c24xx_eint_data *data = irq_desc_get_handler_data(desc); - struct irq_chip *chip = irq_desc_get_irq_chip(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); struct samsung_pinctrl_drv_data *d = data->drvdata; unsigned int pend, mask; @@ -388,12 +388,12 @@ static inline void s3c24xx_demux_eint(struct irq_desc *desc, chained_irq_exit(chip, desc); } -static void s3c24xx_demux_eint4_7(unsigned int irq, struct irq_desc *desc) +static void s3c24xx_demux_eint4_7(struct irq_desc *desc) { s3c24xx_demux_eint(desc, 0, 0xf0); } -static void s3c24xx_demux_eint8_23(unsigned int irq, struct irq_desc *desc) +static void s3c24xx_demux_eint8_23(struct irq_desc *desc) { s3c24xx_demux_eint(desc, 8, 0xffff00); } diff --git a/drivers/pinctrl/samsung/pinctrl-s3c64xx.c b/drivers/pinctrl/samsung/pinctrl-s3c64xx.c index f5ea40a69711..43407ab248f5 100644 --- a/drivers/pinctrl/samsung/pinctrl-s3c64xx.c +++ b/drivers/pinctrl/samsung/pinctrl-s3c64xx.c @@ -407,7 +407,7 @@ static const struct irq_domain_ops s3c64xx_gpio_irqd_ops = { .xlate = irq_domain_xlate_twocell, }; -static void s3c64xx_eint_gpio_irq(unsigned int irq, struct irq_desc *desc) +static void s3c64xx_eint_gpio_irq(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct s3c64xx_eint_gpio_data *data = irq_desc_get_handler_data(desc); @@ -631,22 +631,22 @@ static inline void s3c64xx_irq_demux_eint(struct irq_desc *desc, u32 range) chained_irq_exit(chip, desc); } -static void s3c64xx_demux_eint0_3(unsigned int irq, struct irq_desc *desc) +static void s3c64xx_demux_eint0_3(struct irq_desc *desc) { s3c64xx_irq_demux_eint(desc, 0xf); } -static void s3c64xx_demux_eint4_11(unsigned int irq, struct irq_desc *desc) +static void s3c64xx_demux_eint4_11(struct irq_desc *desc) { s3c64xx_irq_demux_eint(desc, 0xff0); } -static void s3c64xx_demux_eint12_19(unsigned int irq, struct irq_desc *desc) +static void s3c64xx_demux_eint12_19(struct irq_desc *desc) { s3c64xx_irq_demux_eint(desc, 0xff000); } -static void s3c64xx_demux_eint20_27(unsigned int irq, struct irq_desc *desc) +static void s3c64xx_demux_eint20_27(struct irq_desc *desc) { s3c64xx_irq_demux_eint(desc, 0xff00000); } diff --git a/drivers/pinctrl/sirf/pinctrl-atlas7.c b/drivers/pinctrl/sirf/pinctrl-atlas7.c index 9df0c5f25824..0d24d9e4b70c 100644 --- a/drivers/pinctrl/sirf/pinctrl-atlas7.c +++ b/drivers/pinctrl/sirf/pinctrl-atlas7.c @@ -4489,7 +4489,7 @@ static struct irq_chip atlas7_gpio_irq_chip = { .irq_set_type = atlas7_gpio_irq_type, }; -static void atlas7_gpio_handle_irq(unsigned int __irq, struct irq_desc *desc) +static void atlas7_gpio_handle_irq(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct atlas7_gpio_chip *a7gc = to_atlas7_gpio(gc); @@ -4512,7 +4512,7 @@ static void atlas7_gpio_handle_irq(unsigned int __irq, struct irq_desc *desc) if (!status) { pr_warn("%s: gpio [%s] status %#x no interrupt is flaged\n", __func__, gc->label, status); - handle_bad_irq(irq, desc); + handle_bad_irq(desc); return; } diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c index f8bd9fb52033..2a8d69725de8 100644 --- a/drivers/pinctrl/sirf/pinctrl-sirf.c +++ b/drivers/pinctrl/sirf/pinctrl-sirf.c @@ -545,7 +545,7 @@ static struct irq_chip sirfsoc_irq_chip = { .irq_set_type = sirfsoc_gpio_irq_type, }; -static void sirfsoc_gpio_handle_irq(unsigned int __irq, struct irq_desc *desc) +static void sirfsoc_gpio_handle_irq(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct gpio_chip *gc = irq_desc_get_handler_data(desc); @@ -570,7 +570,7 @@ static void sirfsoc_gpio_handle_irq(unsigned int __irq, struct irq_desc *desc) printk(KERN_WARNING "%s: gpio id %d status %#x no interrupt is flagged\n", __func__, bank->id, status); - handle_bad_irq(irq, desc); + handle_bad_irq(desc); return; } diff --git a/drivers/pinctrl/spear/pinctrl-plgpio.c b/drivers/pinctrl/spear/pinctrl-plgpio.c index ae8f29fb5536..1f0af250dbb5 100644 --- a/drivers/pinctrl/spear/pinctrl-plgpio.c +++ b/drivers/pinctrl/spear/pinctrl-plgpio.c @@ -356,7 +356,7 @@ static struct irq_chip plgpio_irqchip = { .irq_set_type = plgpio_irq_set_type, }; -static void plgpio_irq_handler(unsigned irq, struct irq_desc *desc) +static void plgpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct plgpio *plgpio = container_of(gc, struct plgpio, chip); diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index fb4669c0ce0e..38e0c7bdd2ac 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -617,13 +617,11 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, unsigned int type) spin_lock_irqsave(&pctl->lock, flags); if (type & IRQ_TYPE_LEVEL_MASK) - __irq_set_chip_handler_name_locked(d->irq, - &sunxi_pinctrl_level_irq_chip, - handle_fasteoi_irq, NULL); + irq_set_chip_handler_name_locked(d, &sunxi_pinctrl_level_irq_chip, + handle_fasteoi_irq, NULL); else - __irq_set_chip_handler_name_locked(d->irq, - &sunxi_pinctrl_edge_irq_chip, - handle_edge_irq, NULL); + irq_set_chip_handler_name_locked(d, &sunxi_pinctrl_edge_irq_chip, + handle_edge_irq, NULL); regval = readl(pctl->membase + reg); regval &= ~(IRQ_CFG_IRQ_MASK << index); @@ -742,7 +740,7 @@ static struct irq_domain_ops sunxi_pinctrl_irq_domain_ops = { .xlate = sunxi_pinctrl_irq_of_xlate, }; -static void sunxi_pinctrl_irq_handler(unsigned __irq, struct irq_desc *desc) +static void sunxi_pinctrl_irq_handler(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct irq_chip *chip = irq_desc_get_chip(desc); diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 1ef02daddb60..460fa6708bfc 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -346,8 +346,7 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal) * as late as the polling interval is since we can't do that in the respective * accessors of the module parameters. */ -static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, - unsigned long *t) +static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, int *t) { int temp, err = 0; @@ -453,7 +452,7 @@ static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip, } static int acerhdf_get_trip_hyst(struct thermal_zone_device *thermal, int trip, - unsigned long *temp) + int *temp) { if (trip != 0) return -EINVAL; @@ -464,7 +463,7 @@ static int acerhdf_get_trip_hyst(struct thermal_zone_device *thermal, int trip, } static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip, - unsigned long *temp) + int *temp) { if (trip == 0) *temp = fanon; @@ -477,7 +476,7 @@ static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip, } static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal, - unsigned long *temperature) + int *temperature) { *temperature = ACERHDF_TEMP_CRIT; return 0; diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index abdaed34c728..131fee2b093e 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -128,6 +128,24 @@ static const struct dmi_system_id asus_quirks[] = { }, { .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X456UA", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X456UA"), + }, + .driver_data = &quirk_asus_wapf4, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X456UF", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X456UF"), + }, + .driver_data = &quirk_asus_wapf4, + }, + { + .callback = dmi_matched, .ident = "ASUSTeK COMPUTER INC. X501U", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 06697315a088..fb4dd7b3ee71 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -54,8 +54,9 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); #define HPWMI_HARDWARE_QUERY 0x4 #define HPWMI_WIRELESS_QUERY 0x5 #define HPWMI_BIOS_QUERY 0x9 +#define HPWMI_FEATURE_QUERY 0xb #define HPWMI_HOTKEY_QUERY 0xc -#define HPWMI_FEATURE_QUERY 0xd +#define HPWMI_FEATURE2_QUERY 0xd #define HPWMI_WIRELESS2_QUERY 0x1b #define HPWMI_POSTCODEERROR_QUERY 0x2a @@ -295,25 +296,33 @@ static int hp_wmi_tablet_state(void) return (state & 0x4) ? 1 : 0; } -static int __init hp_wmi_bios_2009_later(void) +static int __init hp_wmi_bios_2008_later(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, 0, &state, sizeof(state), sizeof(state)); - if (ret) - return ret; + if (!ret) + return 1; - return (state & 0x10) ? 1 : 0; + return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; } -static int hp_wmi_enable_hotkeys(void) +static int __init hp_wmi_bios_2009_later(void) { - int ret; - int query = 0x6e; + int state = 0; + int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, 0, &state, + sizeof(state), sizeof(state)); + if (!ret) + return 1; - ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query), - 0); + return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; +} +static int __init hp_wmi_enable_hotkeys(void) +{ + int value = 0x6e; + int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &value, + sizeof(value), 0); if (ret) return -EINVAL; return 0; @@ -663,7 +672,7 @@ static int __init hp_wmi_input_setup(void) hp_wmi_tablet_state()); input_sync(hp_wmi_input_dev); - if (hp_wmi_bios_2009_later() == 4) + if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later()) hp_wmi_enable_hotkeys(); status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 0944e834af8d..9f713b832ba3 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -132,7 +132,7 @@ static int is_valid_adc(uint16_t adc_val, uint16_t min, uint16_t max) * to achieve very close approximate temp value with less than * 0.5C error */ -static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp) +static int adc_to_temp(int direct, uint16_t adc_val, int *tp) { int temp; @@ -174,14 +174,13 @@ static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp) * * Can sleep */ -static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp) +static int mid_read_temp(struct thermal_zone_device *tzd, int *temp) { struct thermal_device_info *td_info = tzd->devdata; uint16_t adc_val, addr; uint8_t data = 0; int ret; - unsigned long curr_temp; - + int curr_temp; addr = td_info->chnl_addr; @@ -453,7 +452,7 @@ static SIMPLE_DEV_PM_OPS(mid_thermal_pm, * * Can sleep */ -static int read_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp) +static int read_curr_temp(struct thermal_zone_device *tzd, int *temp) { WARN_ON(tzd == NULL); return mid_read_temp(tzd, temp); diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 6740c513919c..f2372f400ddb 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -938,7 +938,7 @@ static int toshiba_usb_sleep_music_get(struct toshiba_acpi_dev *dev, u32 *state) else if (result == TOS_NOT_SUPPORTED) return -ENODEV; - return result = TOS_SUCCESS ? 0 : -EIO; + return result == TOS_SUCCESS ? 0 : -EIO; } static int toshiba_usb_sleep_music_set(struct toshiba_acpi_dev *dev, u32 state) @@ -2398,11 +2398,9 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) if (error) return error; - error = toshiba_hotkey_event_type_get(dev, &events_type); - if (error) { - pr_err("Unable to query Hotkey Event Type\n"); - return error; - } + if (toshiba_hotkey_event_type_get(dev, &events_type)) + pr_notice("Unable to query Hotkey Event Type\n"); + dev->hotkey_event_type = events_type; dev->hotkey_dev = input_allocate_device(); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index aac47573f9ed..eb391a281833 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -194,34 +194,6 @@ static bool wmi_parse_guid(const u8 *src, u8 *dest) return true; } -/* - * Convert a raw GUID to the ACII string representation - */ -static int wmi_gtoa(const char *in, char *out) -{ - int i; - - for (i = 3; i >= 0; i--) - out += sprintf(out, "%02X", in[i] & 0xFF); - - out += sprintf(out, "-"); - out += sprintf(out, "%02X", in[5] & 0xFF); - out += sprintf(out, "%02X", in[4] & 0xFF); - out += sprintf(out, "-"); - out += sprintf(out, "%02X", in[7] & 0xFF); - out += sprintf(out, "%02X", in[6] & 0xFF); - out += sprintf(out, "-"); - out += sprintf(out, "%02X", in[8] & 0xFF); - out += sprintf(out, "%02X", in[9] & 0xFF); - out += sprintf(out, "-"); - - for (i = 10; i <= 15; i++) - out += sprintf(out, "%02X", in[i] & 0xFF); - - *out = '\0'; - return 0; -} - static bool find_guid(const char *guid_string, struct wmi_block **out) { char tmp[16], guid_input[16]; @@ -457,11 +429,7 @@ EXPORT_SYMBOL_GPL(wmi_set_block); static void wmi_dump_wdg(const struct guid_block *g) { - char guid_string[37]; - - wmi_gtoa(g->guid, guid_string); - - pr_info("%s:\n", guid_string); + pr_info("%pUL:\n", g->guid); pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); pr_info("\tnotify_id: %02X\n", g->notify_id); pr_info("\treserved: %02X\n", g->reserved); @@ -661,7 +629,6 @@ EXPORT_SYMBOL_GPL(wmi_has_guid); static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { - char guid_string[37]; struct wmi_block *wblock; wblock = dev_get_drvdata(dev); @@ -670,9 +637,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, return strlen(buf); } - wmi_gtoa(wblock->gblock.guid, guid_string); - - return sprintf(buf, "wmi:%s\n", guid_string); + return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid); } static DEVICE_ATTR_RO(modalias); @@ -695,7 +660,7 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) if (!wblock) return -ENOMEM; - wmi_gtoa(wblock->gblock.guid, guid_string); + sprintf(guid_string, "%pUL", wblock->gblock.guid); strcpy(&env->buf[env->buflen - 1], "wmi:"); memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); @@ -721,12 +686,9 @@ static struct class wmi_class = { static int wmi_create_device(const struct guid_block *gblock, struct wmi_block *wblock, acpi_handle handle) { - char guid_string[37]; - wblock->dev.class = &wmi_class; - wmi_gtoa(gblock->guid, guid_string); - dev_set_name(&wblock->dev, "%s", guid_string); + dev_set_name(&wblock->dev, "%pUL", gblock->guid); dev_set_drvdata(&wblock->dev, wblock); @@ -877,7 +839,6 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) struct guid_block *block; struct wmi_block *wblock; struct list_head *p; - char guid_string[37]; list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); @@ -888,8 +849,8 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) if (wblock->handler) wblock->handler(event, wblock->handler_data); if (debug_event) { - wmi_gtoa(wblock->gblock.guid, guid_string); - pr_info("DEBUG Event GUID: %s\n", guid_string); + pr_info("DEBUG Event GUID: %pUL\n", + wblock->gblock.guid); } acpi_bus_generate_netlink_event( diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 1c202ccbd2a6..907293e6f2a4 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -619,7 +619,7 @@ static int cm_get_battery_temperature(struct charger_manager *cm, #ifdef CONFIG_THERMAL if (cm->tzd_batt) { - ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp); + ret = thermal_zone_get_temp(cm->tzd_batt, temp); if (!ret) /* Calibrate temperature unit */ *temp /= 100; diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 869284c2e1e8..456987c88baa 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -557,7 +557,7 @@ EXPORT_SYMBOL_GPL(power_supply_unreg_notifier); #ifdef CONFIG_THERMAL static int power_supply_read_temp(struct thermal_zone_device *tzd, - unsigned long *temp) + int *temp) { struct power_supply *psy; union power_supply_propval val; diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index f4f2c1f76c32..74f2d3ff1d7c 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -91,7 +91,7 @@ #define TWL4030_MSTATEC_COMPLETE1 0x0b #define TWL4030_MSTATEC_COMPLETE4 0x0e -#if IS_ENABLED(CONFIG_TWL4030_MADC) +#if IS_REACHABLE(CONFIG_TWL4030_MADC) /* * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11) * then AC is available. @@ -1057,13 +1057,9 @@ static int twl4030_bci_probe(struct platform_device *pdev) phynode = of_find_compatible_node(bci->dev->of_node->parent, NULL, "ti,twl4030-usb"); - if (phynode) { + if (phynode) bci->transceiver = devm_usb_get_phy_by_node( bci->dev, phynode, &bci->usb_nb); - if (IS_ERR(bci->transceiver) && - PTR_ERR(bci->transceiver) == -EPROBE_DEFER) - return -EPROBE_DEFER; - } } /* Enable interrupts now. */ diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 738adfa5332b..52ea605f8130 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -318,6 +318,7 @@ static const struct of_device_id of_anatop_regulator_match_tbl[] = { { .compatible = "fsl,anatop-regulator", }, { /* end */ } }; +MODULE_DEVICE_TABLE(of, of_anatop_regulator_match_tbl); static struct platform_driver anatop_regulator_driver = { .driver = { diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 7a85ac9e32c5..7849187d91ae 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1394,15 +1394,15 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) return 0; r = regulator_dev_lookup(dev, rdev->supply_name, &ret); - if (ret == -ENODEV) { - /* - * No supply was specified for this regulator and - * there will never be one. - */ - return 0; - } - if (!r) { + if (ret == -ENODEV) { + /* + * No supply was specified for this regulator and + * there will never be one. + */ + return 0; + } + if (have_full_constraints()) { r = dummy_regulator_rdev; } else { @@ -1422,11 +1422,10 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) return ret; /* Cascade always-on state to supply */ - if (_regulator_is_enabled(rdev)) { + if (_regulator_is_enabled(rdev) && rdev->supply) { ret = regulator_enable(rdev->supply); if (ret < 0) { - if (rdev->supply) - _regulator_put(rdev->supply); + _regulator_put(rdev->supply); return ret; } } diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 464018de7e97..7bba8b747f30 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -394,6 +394,7 @@ static const struct of_device_id regulator_gpio_of_match[] = { { .compatible = "regulator-gpio", }, {}, }; +MODULE_DEVICE_TABLE(of, regulator_gpio_of_match); #endif static struct platform_driver gpio_regulator_driver = { diff --git a/drivers/regulator/pbias-regulator.c b/drivers/regulator/pbias-regulator.c index 4fa7bcaf454e..f9d74d63be7c 100644 --- a/drivers/regulator/pbias-regulator.c +++ b/drivers/regulator/pbias-regulator.c @@ -45,6 +45,10 @@ struct pbias_regulator_data { int voltage; }; +struct pbias_of_data { + unsigned int offset; +}; + static const unsigned int pbias_volt_table[] = { 1800000, 3000000 @@ -102,8 +106,35 @@ static struct of_regulator_match pbias_matches[] = { }; #define PBIAS_NUM_REGS ARRAY_SIZE(pbias_matches) +/* Offset from SCM general area (and syscon) base */ + +static const struct pbias_of_data pbias_of_data_omap2 = { + .offset = 0x230, +}; + +static const struct pbias_of_data pbias_of_data_omap3 = { + .offset = 0x2b0, +}; + +static const struct pbias_of_data pbias_of_data_omap4 = { + .offset = 0x60, +}; + +static const struct pbias_of_data pbias_of_data_omap5 = { + .offset = 0x60, +}; + +static const struct pbias_of_data pbias_of_data_dra7 = { + .offset = 0xe00, +}; + static const struct of_device_id pbias_of_match[] = { { .compatible = "ti,pbias-omap", }, + { .compatible = "ti,pbias-omap2", .data = &pbias_of_data_omap2, }, + { .compatible = "ti,pbias-omap3", .data = &pbias_of_data_omap3, }, + { .compatible = "ti,pbias-omap4", .data = &pbias_of_data_omap4, }, + { .compatible = "ti,pbias-omap5", .data = &pbias_of_data_omap5, }, + { .compatible = "ti,pbias-dra7", .data = &pbias_of_data_dra7, }, {}, }; MODULE_DEVICE_TABLE(of, pbias_of_match); @@ -118,6 +149,9 @@ static int pbias_regulator_probe(struct platform_device *pdev) const struct pbias_reg_info *info; int ret = 0; int count, idx, data_idx = 0; + const struct of_device_id *match; + const struct pbias_of_data *data; + unsigned int offset; count = of_regulator_match(&pdev->dev, np, pbias_matches, PBIAS_NUM_REGS); @@ -133,6 +167,20 @@ static int pbias_regulator_probe(struct platform_device *pdev) if (IS_ERR(syscon)) return PTR_ERR(syscon); + match = of_match_device(of_match_ptr(pbias_of_match), &pdev->dev); + if (match && match->data) { + data = match->data; + offset = data->offset; + } else { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + offset = res->start; + dev_WARN(&pdev->dev, + "using legacy dt data for pbias offset\n"); + } + cfg.regmap = syscon; cfg.dev = &pdev->dev; @@ -145,10 +193,6 @@ static int pbias_regulator_probe(struct platform_device *pdev) if (!info) return -ENODEV; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - drvdata[data_idx].syscon = syscon; drvdata[data_idx].info = info; drvdata[data_idx].desc.name = info->name; @@ -158,9 +202,9 @@ static int pbias_regulator_probe(struct platform_device *pdev) drvdata[data_idx].desc.volt_table = pbias_volt_table; drvdata[data_idx].desc.n_voltages = 2; drvdata[data_idx].desc.enable_time = info->enable_time; - drvdata[data_idx].desc.vsel_reg = res->start; + drvdata[data_idx].desc.vsel_reg = offset; drvdata[data_idx].desc.vsel_mask = info->vmode; - drvdata[data_idx].desc.enable_reg = res->start; + drvdata[data_idx].desc.enable_reg = offset; drvdata[data_idx].desc.enable_mask = info->enable_mask; drvdata[data_idx].desc.enable_val = info->enable; drvdata[data_idx].desc.disable_val = info->disable_val; diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c index 7f97223f95c5..a02c1b961039 100644 --- a/drivers/regulator/tps65218-regulator.c +++ b/drivers/regulator/tps65218-regulator.c @@ -73,7 +73,7 @@ static const struct regulator_linear_range dcdc4_ranges[] = { }; static struct tps_info tps65218_pmic_regs[] = { - TPS65218_INFO(DCDC1, "DCDC1", 850000, 167500), + TPS65218_INFO(DCDC1, "DCDC1", 850000, 1675000), TPS65218_INFO(DCDC2, "DCDC2", 850000, 1675000), TPS65218_INFO(DCDC3, "DCDC3", 900000, 3400000), TPS65218_INFO(DCDC4, "DCDC4", 1175000, 3400000), diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c index bed9d3ee4198..c810cbbd463f 100644 --- a/drivers/regulator/vexpress.c +++ b/drivers/regulator/vexpress.c @@ -103,6 +103,7 @@ static const struct of_device_id vexpress_regulator_of_match[] = { { .compatible = "arm,vexpress-volt", }, { } }; +MODULE_DEVICE_TABLE(of, vexpress_regulator_of_match); static struct platform_driver vexpress_regulator_driver = { .probe = vexpress_regulator_probe, diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index b7d60306b0bc..fc94bfdceb95 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -229,7 +229,7 @@ static struct mon_msg *mon_next_message(struct mon_private *monpriv) /****************************************************************************** * IUCV handler * *****************************************************************************/ -static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16]) +static void mon_iucv_path_complete(struct iucv_path *path, u8 *ipuser) { struct mon_private *monpriv = path->private; @@ -237,7 +237,7 @@ static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16]) wake_up(&mon_conn_wait_queue); } -static void mon_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) +static void mon_iucv_path_severed(struct iucv_path *path, u8 *ipuser) { struct mon_private *monpriv = path->private; diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 9bb48d70957c..799c1524c779 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -99,8 +99,8 @@ static const struct file_operations vmlogrdr_fops = { }; -static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 ipuser[16]); -static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 ipuser[16]); +static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 *ipuser); +static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 *ipuser); static void vmlogrdr_iucv_message_pending(struct iucv_path *, struct iucv_message *); @@ -160,7 +160,7 @@ static struct cdev *vmlogrdr_cdev = NULL; static int recording_class_AB; -static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16]) +static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 *ipuser) { struct vmlogrdr_priv_t * logptr = path->private; @@ -171,7 +171,7 @@ static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16]) } -static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) +static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 *ipuser) { struct vmlogrdr_priv_t * logptr = path->private; u8 reason = (u8) ipuser[8]; diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 848e3b64ea6e..4bb5262f7aee 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -319,6 +319,8 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit, int retries = 0, cc; unsigned long laob = 0; + WARN_ON_ONCE(aob && ((queue_type(q) != QDIO_IQDIO_QFMT) || + !q->u.out.use_cq)); if (q->u.out.use_cq && aob != 0) { fc = QDIO_SIGA_WRITEQ; laob = aob; @@ -329,8 +331,6 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit, fc |= QDIO_SIGA_QEBSM_FLAG; } again: - WARN_ON_ONCE((aob && queue_type(q) != QDIO_IQDIO_QFMT) || - (aob && fc != QDIO_SIGA_WRITEQ)); cc = do_siga_output(schid, q->mask, busy_bit, fc, laob); /* hipersocket busy condition */ diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 33f7040d711d..0ba3a2f81750 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -149,12 +149,11 @@ static struct device_driver netiucv_driver = { .pm = &netiucv_pm_ops, }; -static int netiucv_callback_connreq(struct iucv_path *, - u8 ipvmid[8], u8 ipuser[16]); -static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]); -static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]); -static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]); -static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]); +static int netiucv_callback_connreq(struct iucv_path *, u8 *, u8 *); +static void netiucv_callback_connack(struct iucv_path *, u8 *); +static void netiucv_callback_connrej(struct iucv_path *, u8 *); +static void netiucv_callback_connsusp(struct iucv_path *, u8 *); +static void netiucv_callback_connres(struct iucv_path *, u8 *); static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *); static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *); @@ -556,8 +555,8 @@ static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16]) fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn); } -static int netiucv_callback_connreq(struct iucv_path *path, - u8 ipvmid[8], u8 ipuser[16]) +static int netiucv_callback_connreq(struct iucv_path *path, u8 *ipvmid, + u8 *ipuser) { struct iucv_connection *conn = path->private; struct iucv_event ev; @@ -587,21 +586,21 @@ static int netiucv_callback_connreq(struct iucv_path *path, return rc; } -static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16]) +static void netiucv_callback_connrej(struct iucv_path *path, u8 *ipuser) { struct iucv_connection *conn = path->private; fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn); } -static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16]) +static void netiucv_callback_connsusp(struct iucv_path *path, u8 *ipuser) { struct iucv_connection *conn = path->private; fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn); } -static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16]) +static void netiucv_callback_connres(struct iucv_path *path, u8 *ipuser) { struct iucv_connection *conn = path->private; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index ba974a2e409f..1766a20ebcb1 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -18,6 +18,7 @@ #include <linux/bitops.h> #include <linux/seq_file.h> #include <linux/ethtool.h> +#include <linux/hashtable.h> #include <net/ipv6.h> #include <net/if_inet6.h> @@ -663,9 +664,7 @@ struct qeth_card_info { char mcl_level[QETH_MCL_LENGTH + 1]; int guestlan; int mac_bits; - int portname_required; int portno; - char portname[9]; enum qeth_card_types type; enum qeth_link_types link_type; int is_multicast_different; @@ -741,11 +740,17 @@ struct qeth_vlan_vid { unsigned short vid; }; -struct qeth_mc_mac { - struct list_head list; - __u8 mc_addr[MAX_ADDR_LEN]; - unsigned char mc_addrlen; - int is_vmac; +enum qeth_mac_disposition { + QETH_DISP_MAC_DELETE = 0, + QETH_DISP_MAC_DO_NOTHING = 1, + QETH_DISP_MAC_ADD = 2, +}; + +struct qeth_mac { + u8 mac_addr[OSA_ADDR_LEN]; + u8 is_uc:1; + u8 disp_flag:2; + struct hlist_node hnode; }; struct qeth_rx { @@ -792,7 +797,7 @@ struct qeth_card { spinlock_t mclock; unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; struct list_head vid_list; - struct list_head mc_list; + DECLARE_HASHTABLE(mac_htable, 4); struct work_struct kernel_thread_starter; spinlock_t thread_mask_lock; unsigned long thread_start_mask; @@ -969,6 +974,15 @@ int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action); int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot); void qeth_trace_features(struct qeth_card *); void qeth_close_dev(struct qeth_card *); +int qeth_send_simple_setassparms(struct qeth_card *, enum qeth_ipa_funcs, + __u16, long); +int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, __u16, + long, + int (*reply_cb)(struct qeth_card *, + struct qeth_reply *, unsigned long), + void *); +int qeth_start_ipa_tx_checksum(struct qeth_card *); +int qeth_set_rx_csum(struct qeth_card *, int); /* exports for OSN */ int qeth_osn_assist(struct net_device *, void *, int); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 5e20fba37bff..31ac53fa5cee 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1982,14 +1982,6 @@ static void qeth_idx_read_cb(struct qeth_channel *channel, goto out; } -/** - * * temporary fix for microcode bug - * * to revert it,replace OR by AND - * */ - if ((!QETH_IDX_NO_PORTNAME_REQUIRED(iob->data)) || - (card->info.type == QETH_CARD_TYPE_OSD)) - card->info.portname_required = 1; - memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); if (temp != qeth_peer_func_level(card->info.func_level)) { QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function " @@ -2360,8 +2352,6 @@ static int qeth_ulp_enable(struct qeth_card *card) &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data), &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH); - memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(iob->data), - card->info.portname, 9); rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob, qeth_ulp_enable_cb, NULL); return rc; @@ -2680,48 +2670,6 @@ out_qdio: return rc; } -static void qeth_print_status_with_portname(struct qeth_card *card) -{ - char dbf_text[15]; - int i; - - sprintf(dbf_text, "%s", card->info.portname + 1); - for (i = 0; i < 8; i++) - dbf_text[i] = - (char) _ebcasc[(__u8) dbf_text[i]]; - dbf_text[8] = 0; - dev_info(&card->gdev->dev, "Device is a%s card%s%s%s\n" - "with link type %s (portname: %s)\n", - qeth_get_cardname(card), - (card->info.mcl_level[0]) ? " (level: " : "", - (card->info.mcl_level[0]) ? card->info.mcl_level : "", - (card->info.mcl_level[0]) ? ")" : "", - qeth_get_cardname_short(card), - dbf_text); - -} - -static void qeth_print_status_no_portname(struct qeth_card *card) -{ - if (card->info.portname[0]) - dev_info(&card->gdev->dev, "Device is a%s " - "card%s%s%s\nwith link type %s " - "(no portname needed by interface).\n", - qeth_get_cardname(card), - (card->info.mcl_level[0]) ? " (level: " : "", - (card->info.mcl_level[0]) ? card->info.mcl_level : "", - (card->info.mcl_level[0]) ? ")" : "", - qeth_get_cardname_short(card)); - else - dev_info(&card->gdev->dev, "Device is a%s " - "card%s%s%s\nwith link type %s.\n", - qeth_get_cardname(card), - (card->info.mcl_level[0]) ? " (level: " : "", - (card->info.mcl_level[0]) ? card->info.mcl_level : "", - (card->info.mcl_level[0]) ? ")" : "", - qeth_get_cardname_short(card)); -} - void qeth_print_status_message(struct qeth_card *card) { switch (card->info.type) { @@ -2758,10 +2706,13 @@ void qeth_print_status_message(struct qeth_card *card) default: memset(&card->info.mcl_level[0], 0, QETH_MCL_LENGTH + 1); } - if (card->info.portname_required) - qeth_print_status_with_portname(card); - else - qeth_print_status_no_portname(card); + dev_info(&card->gdev->dev, + "Device is a%s card%s%s%s\nwith link type %s.\n", + qeth_get_cardname(card), + (card->info.mcl_level[0]) ? " (level: " : "", + (card->info.mcl_level[0]) ? card->info.mcl_level : "", + (card->info.mcl_level[0]) ? ")" : "", + qeth_get_cardname_short(card)); } EXPORT_SYMBOL_GPL(qeth_print_status_message); @@ -5027,13 +4978,11 @@ static void qeth_core_free_card(struct qeth_card *card) void qeth_trace_features(struct qeth_card *card) { QETH_CARD_TEXT(card, 2, "features"); - QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.supported_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.enabled_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.supported_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.enabled_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.supported_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.enabled_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->info.diagass_support); + QETH_CARD_HEX(card, 2, &card->options.ipa4, sizeof(card->options.ipa4)); + QETH_CARD_HEX(card, 2, &card->options.ipa6, sizeof(card->options.ipa6)); + QETH_CARD_HEX(card, 2, &card->options.adp, sizeof(card->options.adp)); + QETH_CARD_HEX(card, 2, &card->info.diagass_support, + sizeof(card->info.diagass_support)); } EXPORT_SYMBOL_GPL(qeth_trace_features); @@ -5132,6 +5081,7 @@ retriable: } card->options.ipa4.supported_funcs = 0; + card->options.ipa6.supported_funcs = 0; card->options.adp.supported_funcs = 0; card->options.sbp.supported_funcs = 0; card->info.diagass_support = 0; @@ -5317,6 +5267,102 @@ no_mem: } EXPORT_SYMBOL_GPL(qeth_core_get_next_skb); +static int qeth_setassparms_cb(struct qeth_card *card, + struct qeth_reply *reply, unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + + QETH_CARD_TEXT(card, 4, "defadpcb"); + + cmd = (struct qeth_ipa_cmd *) data; + if (cmd->hdr.return_code == 0) { + cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; + if (cmd->hdr.prot_version == QETH_PROT_IPV4) + card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; + if (cmd->hdr.prot_version == QETH_PROT_IPV6) + card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; + } + if (cmd->data.setassparms.hdr.assist_no == IPA_INBOUND_CHECKSUM && + cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { + card->info.csum_mask = cmd->data.setassparms.data.flags_32bit; + QETH_CARD_TEXT_(card, 3, "csum:%d", card->info.csum_mask); + } + if (cmd->data.setassparms.hdr.assist_no == IPA_OUTBOUND_CHECKSUM && + cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { + card->info.tx_csum_mask = + cmd->data.setassparms.data.flags_32bit; + QETH_CARD_TEXT_(card, 3, "tcsu:%d", card->info.tx_csum_mask); + } + + return 0; +} + +static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, + __u16 cmd_code, __u16 len, + enum qeth_prot_versions prot) +{ + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + + QETH_CARD_TEXT(card, 4, "getasscm"); + iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot); + + if (iob) { + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setassparms.hdr.assist_no = ipa_func; + cmd->data.setassparms.hdr.length = 8 + len; + cmd->data.setassparms.hdr.command_code = cmd_code; + cmd->data.setassparms.hdr.return_code = 0; + cmd->data.setassparms.hdr.seq_no = 0; + } + + return iob; +} + +int qeth_send_setassparms(struct qeth_card *card, + struct qeth_cmd_buffer *iob, __u16 len, long data, + int (*reply_cb)(struct qeth_card *, + struct qeth_reply *, unsigned long), + void *reply_param) +{ + int rc; + struct qeth_ipa_cmd *cmd; + + QETH_CARD_TEXT(card, 4, "sendassp"); + + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + if (len <= sizeof(__u32)) + cmd->data.setassparms.data.flags_32bit = (__u32) data; + else /* (len > sizeof(__u32)) */ + memcpy(&cmd->data.setassparms.data, (void *) data, len); + + rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param); + return rc; +} +EXPORT_SYMBOL_GPL(qeth_send_setassparms); + +int qeth_send_simple_setassparms(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, + __u16 cmd_code, long data) +{ + int rc; + int length = 0; + struct qeth_cmd_buffer *iob; + + QETH_CARD_TEXT(card, 4, "simassp4"); + if (data) + length = sizeof(__u32); + iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, + length, QETH_PROT_IPV4); + if (!iob) + return -ENOMEM; + rc = qeth_send_setassparms(card, iob, length, data, + qeth_setassparms_cb, NULL); + return rc; +} +EXPORT_SYMBOL_GPL(qeth_send_simple_setassparms); + static void qeth_unregister_dbf_views(void) { int x; @@ -6003,6 +6049,75 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev, } EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings); +static int qeth_send_checksum_command(struct qeth_card *card) +{ + int rc; + + rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, + IPA_CMD_ASS_START, 0); + if (rc) { + dev_warn(&card->gdev->dev, "Starting HW checksumming for %s " + "failed, using SW checksumming\n", + QETH_CARD_IFNAME(card)); + return rc; + } + rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, + IPA_CMD_ASS_ENABLE, + card->info.csum_mask); + if (rc) { + dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s " + "failed, using SW checksumming\n", + QETH_CARD_IFNAME(card)); + return rc; + } + return 0; +} + +int qeth_set_rx_csum(struct qeth_card *card, int on) +{ + int rc; + + if (on) { + rc = qeth_send_checksum_command(card); + if (rc) + return -EIO; + dev_info(&card->gdev->dev, + "HW Checksumming (inbound) enabled\n"); + } else { + rc = qeth_send_simple_setassparms(card, + IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0); + if (rc) + return -EIO; + } + return 0; +} +EXPORT_SYMBOL_GPL(qeth_set_rx_csum); + +int qeth_start_ipa_tx_checksum(struct qeth_card *card) +{ + int rc = 0; + + if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) + return rc; + rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, + IPA_CMD_ASS_START, 0); + if (rc) + goto err_out; + rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, + IPA_CMD_ASS_ENABLE, + card->info.tx_csum_mask); + if (rc) + goto err_out; + + dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n"); + return rc; +err_out: + dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s " + "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card)); + return rc; +} +EXPORT_SYMBOL_GPL(qeth_start_ipa_tx_checksum); + static int __init qeth_core_init(void) { int rc; diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index 423bec56cffa..e6e5b9671bf2 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -153,52 +153,17 @@ static DEVICE_ATTR(portno, 0644, qeth_dev_portno_show, qeth_dev_portno_store); static ssize_t qeth_dev_portname_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct qeth_card *card = dev_get_drvdata(dev); - char portname[9] = {0, }; - - if (!card) - return -EINVAL; - - if (card->info.portname_required) { - memcpy(portname, card->info.portname + 1, 8); - EBCASC(portname, 8); - return sprintf(buf, "%s\n", portname); - } else - return sprintf(buf, "no portname required\n"); + return sprintf(buf, "no portname required\n"); } static ssize_t qeth_dev_portname_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qeth_card *card = dev_get_drvdata(dev); - char *tmp; - int i, rc = 0; - - if (!card) - return -EINVAL; - - mutex_lock(&card->conf_mutex); - if ((card->state != CARD_STATE_DOWN) && - (card->state != CARD_STATE_RECOVER)) { - rc = -EPERM; - goto out; - } - tmp = strsep((char **) &buf, "\n"); - if ((strlen(tmp) > 8) || (strlen(tmp) == 0)) { - rc = -EINVAL; - goto out; - } - - card->info.portname[0] = strlen(tmp); - /* for beauty reasons */ - for (i = 1; i < 9; i++) - card->info.portname[i] = ' '; - strcpy(card->info.portname + 1, tmp); - ASCEBC(card->info.portname + 1, 8); -out: - mutex_unlock(&card->conf_mutex); - return rc ? rc : count; + dev_warn_once(&card->gdev->dev, + "portname is deprecated and is ignored\n"); + return count; } static DEVICE_ATTR(portname, 0644, qeth_dev_portname_show, diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index a8556692f632..8f1b091e1732 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -19,7 +19,9 @@ #include <linux/mii.h> #include <linux/ip.h> #include <linux/list.h> - +#include <linux/hash.h> +#include <linux/hashtable.h> +#include <linux/string.h> #include "qeth_core.h" #include "qeth_l2.h" @@ -28,7 +30,7 @@ static int qeth_l2_stop(struct net_device *); static int qeth_l2_send_delmac(struct qeth_card *, __u8 *); static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *, enum qeth_ipa_cmds); -static void qeth_l2_set_multicast_list(struct net_device *); +static void qeth_l2_set_rx_mode(struct net_device *); static int qeth_l2_recover(void *); static void qeth_bridgeport_query_support(struct qeth_card *card); static void qeth_bridge_state_change(struct qeth_card *card, @@ -193,49 +195,44 @@ static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac) return rc; } -static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac) +static inline u32 qeth_l2_mac_hash(const u8 *addr) { - struct qeth_mc_mac *mc; - int rc; - - mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC); + return get_unaligned((u32 *)(&addr[2])); +} - if (!mc) - return; +static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac) +{ - memcpy(mc->mc_addr, mac, OSA_ADDR_LEN); - mc->mc_addrlen = OSA_ADDR_LEN; - mc->is_vmac = vmac; + int rc; - if (vmac) { + if (mac->is_uc) { rc = qeth_setdel_makerc(card, - qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC)); + qeth_l2_send_setdelmac(card, mac->mac_addr, + IPA_CMD_SETVMAC)); } else { rc = qeth_setdel_makerc(card, - qeth_l2_send_setgroupmac(card, mac)); + qeth_l2_send_setgroupmac(card, mac->mac_addr)); } - - if (!rc) - list_add_tail(&mc->list, &card->mc_list); - else - kfree(mc); + return rc; } -static void qeth_l2_del_all_mc(struct qeth_card *card, int del) +static void qeth_l2_del_all_macs(struct qeth_card *card, int del) { - struct qeth_mc_mac *mc, *tmp; + struct qeth_mac *mac; + struct hlist_node *tmp; + int i; spin_lock_bh(&card->mclock); - list_for_each_entry_safe(mc, tmp, &card->mc_list, list) { + hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) { if (del) { - if (mc->is_vmac) - qeth_l2_send_setdelmac(card, mc->mc_addr, - IPA_CMD_DELVMAC); + if (mac->is_uc) + qeth_l2_send_setdelmac(card, mac->mac_addr, + IPA_CMD_DELVMAC); else - qeth_l2_send_delgroupmac(card, mc->mc_addr); + qeth_l2_send_delgroupmac(card, mac->mac_addr); } - list_del(&mc->list); - kfree(mc); + hash_del(&mac->hnode); + kfree(mac); } spin_unlock_bh(&card->mclock); } @@ -252,6 +249,23 @@ static inline int qeth_l2_get_cast_type(struct qeth_card *card, return RTN_UNSPEC; } +static inline void qeth_l2_hdr_csum(struct qeth_card *card, + struct qeth_hdr *hdr, struct sk_buff *skb) +{ + struct iphdr *iph = ip_hdr(skb); + + /* tcph->check contains already the pseudo hdr checksum + * so just set the header flags + */ + if (iph->protocol == IPPROTO_UDP) + hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_UDP; + hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_CSUM_TRANSP_REQ | + QETH_HDR_EXT_CSUM_HDR_REQ; + iph->check = 0; + if (card->options.performance_stats) + card->perf_stats.tx_csum++; +} + static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, struct sk_buff *skb, int cast_type) { @@ -386,10 +400,42 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN); kfree(tmpid); } - qeth_l2_set_multicast_list(card->dev); + qeth_l2_set_rx_mode(card->dev); return rc; } +static netdev_features_t qeth_l2_fix_features(struct net_device *dev, + netdev_features_t features) +{ + struct qeth_card *card = dev->ml_priv; + + QETH_DBF_TEXT(SETUP, 2, "fixfeat"); + if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) + features &= ~NETIF_F_IP_CSUM; + if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) + features &= ~NETIF_F_RXCSUM; + QETH_DBF_HEX(SETUP, 2, &features, sizeof(features)); + return features; +} + +static int qeth_l2_set_features(struct net_device *dev, + netdev_features_t features) +{ + struct qeth_card *card = dev->ml_priv; + netdev_features_t changed = dev->features ^ features; + + QETH_DBF_TEXT(SETUP, 2, "setfeat"); + QETH_DBF_HEX(SETUP, 2, &features, sizeof(features)); + + if (card->state == CARD_STATE_DOWN || + card->state == CARD_STATE_RECOVER) + return 0; + + if (!(changed & NETIF_F_RXCSUM)) + return 0; + return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0); +} + static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) { QETH_DBF_TEXT(SETUP , 2, "stopcard"); @@ -411,7 +457,7 @@ static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) card->state = CARD_STATE_SOFTSETUP; } if (card->state == CARD_STATE_SOFTSETUP) { - qeth_l2_del_all_mc(card, 0); + qeth_l2_del_all_macs(card, 0); qeth_clear_ipacmd_list(card); card->state = CARD_STATE_HARDSETUP; } @@ -450,11 +496,19 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card, case QETH_HEADER_TYPE_LAYER2: skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, skb->dev); - skb->ip_summed = CHECKSUM_NONE; + if ((card->dev->features & NETIF_F_RXCSUM) + && ((hdr->hdr.l2.flags[1] & + (QETH_HDR_EXT_CSUM_HDR_REQ | + QETH_HDR_EXT_CSUM_TRANSP_REQ)) == + (QETH_HDR_EXT_CSUM_HDR_REQ | + QETH_HDR_EXT_CSUM_TRANSP_REQ))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; if (skb->protocol == htons(ETH_P_802_2)) *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno; len = skb->len; - netif_receive_skb(skb); + napi_gro_receive(&card->napi, skb); break; case QETH_HEADER_TYPE_OSN: if (card->info.type == QETH_CARD_TYPE_OSN) { @@ -711,29 +765,91 @@ static void qeth_promisc_to_bridge(struct qeth_card *card) card->options.sbp.role = role; card->info.promisc_mode = promisc_mode; } + } +/* New MAC address is added to the hash table and marked to be written on card + * only if there is not in the hash table storage already + * +*/ +static void +qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha, u8 is_uc) +{ + struct qeth_mac *mac; -static void qeth_l2_set_multicast_list(struct net_device *dev) + hash_for_each_possible(card->mac_htable, mac, hnode, + qeth_l2_mac_hash(ha->addr)) { + if (is_uc == mac->is_uc && + !memcmp(ha->addr, mac->mac_addr, OSA_ADDR_LEN)) { + mac->disp_flag = QETH_DISP_MAC_DO_NOTHING; + return; + } + } + + mac = kzalloc(sizeof(struct qeth_mac), GFP_ATOMIC); + + if (!mac) + return; + + memcpy(mac->mac_addr, ha->addr, OSA_ADDR_LEN); + mac->is_uc = is_uc; + mac->disp_flag = QETH_DISP_MAC_ADD; + + hash_add(card->mac_htable, &mac->hnode, + qeth_l2_mac_hash(mac->mac_addr)); + +} + +static void qeth_l2_set_rx_mode(struct net_device *dev) { struct qeth_card *card = dev->ml_priv; struct netdev_hw_addr *ha; + struct qeth_mac *mac; + struct hlist_node *tmp; + int i; + int rc; if (card->info.type == QETH_CARD_TYPE_OSN) - return ; + return; QETH_CARD_TEXT(card, 3, "setmulti"); if (qeth_threads_running(card, QETH_RECOVER_THREAD) && (card->state != CARD_STATE_UP)) return; - qeth_l2_del_all_mc(card, 1); + spin_lock_bh(&card->mclock); + netdev_for_each_mc_addr(ha, dev) - qeth_l2_add_mc(card, ha->addr, 0); + qeth_l2_add_mac(card, ha, 0); netdev_for_each_uc_addr(ha, dev) - qeth_l2_add_mc(card, ha->addr, 1); + qeth_l2_add_mac(card, ha, 1); + + hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) { + if (mac->disp_flag == QETH_DISP_MAC_DELETE) { + if (!mac->is_uc) + rc = qeth_l2_send_delgroupmac(card, + mac->mac_addr); + else { + rc = qeth_l2_send_setdelmac(card, mac->mac_addr, + IPA_CMD_DELVMAC); + } + + hash_del(&mac->hnode); + kfree(mac); + + } else if (mac->disp_flag == QETH_DISP_MAC_ADD) { + rc = qeth_l2_write_mac(card, mac); + if (rc) { + hash_del(&mac->hnode); + kfree(mac); + } else + mac->disp_flag = QETH_DISP_MAC_DELETE; + } else + mac->disp_flag = QETH_DISP_MAC_DELETE; + } spin_unlock_bh(&card->mclock); + if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE)) qeth_setadp_promisc_mode(card); else @@ -803,6 +919,8 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) sizeof(struct qeth_hdr)); skb_set_mac_header(new_skb, sizeof(struct qeth_hdr)); qeth_l2_fill_header(card, hdr, new_skb, cast_type); + if (new_skb->ip_summed == CHECKSUM_PARTIAL) + qeth_l2_hdr_csum(card, hdr, new_skb); } } @@ -915,7 +1033,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev) qeth_l2_create_device_attributes(&gdev->dev); INIT_LIST_HEAD(&card->vid_list); - INIT_LIST_HEAD(&card->mc_list); + hash_init(card->mac_htable); card->options.layer2 = 1; card->info.hwtrap = 0; return 0; @@ -961,13 +1079,15 @@ static const struct net_device_ops qeth_l2_netdev_ops = { .ndo_get_stats = qeth_get_stats, .ndo_start_xmit = qeth_l2_hard_start_xmit, .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = qeth_l2_set_multicast_list, + .ndo_set_rx_mode = qeth_l2_set_rx_mode, .ndo_do_ioctl = qeth_l2_do_ioctl, .ndo_set_mac_address = qeth_l2_set_mac_address, .ndo_change_mtu = qeth_change_mtu, .ndo_vlan_rx_add_vid = qeth_l2_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = qeth_l2_vlan_rx_kill_vid, .ndo_tx_timeout = qeth_tx_timeout, + .ndo_fix_features = qeth_l2_fix_features, + .ndo_set_features = qeth_l2_set_features }; static int qeth_l2_setup_netdev(struct qeth_card *card) @@ -997,6 +1117,11 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) (card->info.type != QETH_CARD_TYPE_OSN) ? &qeth_l2_ethtool_ops : &qeth_l2_osn_ops; card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) { + card->dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM; + /* Turn on RX offloading per default */ + card->dev->features |= NETIF_F_RXCSUM; + } card->info.broadcast_capable = 1; qeth_l2_request_initial_mac(card); SET_NETDEV_DEV(card->dev, &card->gdev->dev); @@ -1004,6 +1129,17 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) return register_netdev(card->dev); } +static int qeth_l2_start_ipassists(struct qeth_card *card) +{ + /* configure isolation level */ + if (qeth_set_access_ctrl_online(card, 0)) + return -ENODEV; + if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) + qeth_set_rx_csum(card, 1); + qeth_start_ipa_tx_checksum(card); + return 0; +} + static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); @@ -1069,12 +1205,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) contin: if ((card->info.type == QETH_CARD_TYPE_OSD) || (card->info.type == QETH_CARD_TYPE_OSX)) { - /* configure isolation level */ - rc = qeth_set_access_ctrl_online(card, 0); - if (rc) { - rc = -ENODEV; + if (qeth_l2_start_ipassists(card)) goto out_remove; - } } if (card->info.type != QETH_CARD_TYPE_OSN && @@ -1106,7 +1238,7 @@ contin: rtnl_unlock(); } /* this also sets saved unicast addresses */ - qeth_l2_set_multicast_list(card->dev); + qeth_l2_set_rx_mode(card->dev); } /* let user_space know that device is online */ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); @@ -1452,8 +1584,8 @@ static void qeth_bridge_emit_host_event(struct qeth_card *card, env[i] = str[i]; i++; } if (code & IPA_ADDR_CHANGE_CODE_MACADDR) { - snprintf(str[i], sizeof(str[i]), "MAC=%pM6", - &addr_lnid->mac); + snprintf(str[i], sizeof(str[i]), "MAC=%pM", + addr_lnid->mac); env[i] = str[i]; i++; } snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x", diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c index 52673cd1db99..692db49e3d2a 100644 --- a/drivers/s390/net/qeth_l2_sys.c +++ b/drivers/s390/net/qeth_l2_sys.c @@ -109,7 +109,7 @@ static ssize_t qeth_bridge_port_state_show(struct device *dev, return qeth_bridge_port_role_state_show(dev, attr, buf, 1); } -static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show, +static DEVICE_ATTR(bridge_state, 0444, qeth_bridge_port_state_show, NULL); static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev, diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index a1aaa36e9ebb..543960e96b42 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1065,27 +1065,6 @@ static struct qeth_cmd_buffer *qeth_l3_get_setassparms_cmd( return iob; } -static int qeth_l3_send_setassparms(struct qeth_card *card, - struct qeth_cmd_buffer *iob, __u16 len, long data, - int (*reply_cb)(struct qeth_card *, struct qeth_reply *, - unsigned long), - void *reply_param) -{ - int rc; - struct qeth_ipa_cmd *cmd; - - QETH_CARD_TEXT(card, 4, "sendassp"); - - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - if (len <= sizeof(__u32)) - cmd->data.setassparms.data.flags_32bit = (__u32) data; - else /* (len > sizeof(__u32)) */ - memcpy(&cmd->data.setassparms.data, (void *) data, len); - - rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param); - return rc; -} - #ifdef CONFIG_QETH_IPV6 static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card, enum qeth_ipa_funcs ipa_func, __u16 cmd_code) @@ -1098,31 +1077,12 @@ static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card, 0, QETH_PROT_IPV6); if (!iob) return -ENOMEM; - rc = qeth_l3_send_setassparms(card, iob, 0, 0, + rc = qeth_send_setassparms(card, iob, 0, 0, qeth_l3_default_setassparms_cb, NULL); return rc; } #endif -static int qeth_l3_send_simple_setassparms(struct qeth_card *card, - enum qeth_ipa_funcs ipa_func, __u16 cmd_code, long data) -{ - int rc; - int length = 0; - struct qeth_cmd_buffer *iob; - - QETH_CARD_TEXT(card, 4, "simassp4"); - if (data) - length = sizeof(__u32); - iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code, - length, QETH_PROT_IPV4); - if (!iob) - return -ENOMEM; - rc = qeth_l3_send_setassparms(card, iob, length, data, - qeth_l3_default_setassparms_cb, NULL); - return rc; -} - static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card) { int rc; @@ -1135,8 +1095,8 @@ static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card) QETH_CARD_IFNAME(card)); return 0; } - rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING, - IPA_CMD_ASS_START, 0); + rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_START, 0); if (rc) { dev_warn(&card->gdev->dev, "Starting ARP processing support for %s failed\n", @@ -1158,7 +1118,7 @@ static int qeth_l3_start_ipa_ip_fragmentation(struct qeth_card *card) return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_IP_FRAGMENTATION, + rc = qeth_send_simple_setassparms(card, IPA_IP_FRAGMENTATION, IPA_CMD_ASS_START, 0); if (rc) { dev_warn(&card->gdev->dev, @@ -1183,7 +1143,7 @@ static int qeth_l3_start_ipa_source_mac(struct qeth_card *card) return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_SOURCE_MAC, + rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC, IPA_CMD_ASS_START, 0); if (rc) dev_warn(&card->gdev->dev, @@ -1204,7 +1164,7 @@ static int qeth_l3_start_ipa_vlan(struct qeth_card *card) return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_VLAN_PRIO, + rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO, IPA_CMD_ASS_START, 0); if (rc) { dev_warn(&card->gdev->dev, @@ -1229,7 +1189,7 @@ static int qeth_l3_start_ipa_multicast(struct qeth_card *card) return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_MULTICASTING, + rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING, IPA_CMD_ASS_START, 0); if (rc) { dev_warn(&card->gdev->dev, @@ -1259,7 +1219,7 @@ static int qeth_l3_softsetup_ipv6(struct qeth_card *card) QETH_CARD_IFNAME(card)); return rc; } - rc = qeth_l3_send_simple_setassparms(card, IPA_IPV6, + rc = qeth_send_simple_setassparms(card, IPA_IPV6, IPA_CMD_ASS_START, 3); if (rc) { dev_err(&card->gdev->dev, @@ -1319,7 +1279,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card) rc = -EOPNOTSUPP; goto out; } - rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, IPA_CMD_ASS_START, 0); if (rc) { dev_warn(&card->gdev->dev, "Enabling broadcast filtering for " @@ -1327,7 +1287,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card) goto out; } - rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, IPA_CMD_ASS_CONFIGURE, 1); if (rc) { dev_warn(&card->gdev->dev, @@ -1337,7 +1297,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card) } card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO; dev_info(&card->gdev->dev, "Broadcast enabled\n"); - rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, IPA_CMD_ASS_ENABLE, 1); if (rc) { dev_warn(&card->gdev->dev, "Setting up broadcast echo " @@ -1353,84 +1313,18 @@ out: return rc; } -static int qeth_l3_send_checksum_command(struct qeth_card *card) -{ - int rc; - - rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, - IPA_CMD_ASS_START, 0); - if (rc) { - dev_warn(&card->gdev->dev, "Starting HW checksumming for %s " - "failed, using SW checksumming\n", - QETH_CARD_IFNAME(card)); - return rc; - } - rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, - IPA_CMD_ASS_ENABLE, - card->info.csum_mask); - if (rc) { - dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s " - "failed, using SW checksumming\n", - QETH_CARD_IFNAME(card)); - return rc; - } - return 0; -} - -static int qeth_l3_set_rx_csum(struct qeth_card *card, int on) -{ - int rc = 0; - - if (on) { - rc = qeth_l3_send_checksum_command(card); - if (rc) - return -EIO; - dev_info(&card->gdev->dev, - "HW Checksumming (inbound) enabled\n"); - } else { - rc = qeth_l3_send_simple_setassparms(card, - IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0); - if (rc) - return -EIO; - } - - return 0; -} - -static int qeth_l3_start_ipa_checksum(struct qeth_card *card) +static void qeth_l3_start_ipa_checksum(struct qeth_card *card) { QETH_CARD_TEXT(card, 3, "strtcsum"); - - if (card->dev->features & NETIF_F_RXCSUM) { - rtnl_lock(); - /* force set_features call */ - card->dev->features &= ~NETIF_F_RXCSUM; - netdev_update_features(card->dev); - rtnl_unlock(); - } - return 0; + if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM) + && (card->dev->features & NETIF_F_RXCSUM)) + qeth_set_rx_csum(card, 1); } -static int qeth_l3_start_ipa_tx_checksum(struct qeth_card *card) +static void qeth_l3_start_ipa_tx_checksum(struct qeth_card *card) { - int rc = 0; - - if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) - return rc; - rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, - IPA_CMD_ASS_START, 0); - if (rc) - goto err_out; - rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, - IPA_CMD_ASS_ENABLE, card->info.tx_csum_mask); - if (rc) - goto err_out; - dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n"); - return rc; -err_out: - dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s " - "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card)); - return rc; + QETH_CARD_TEXT(card, 3, "strttxcs"); + qeth_start_ipa_tx_checksum(card); } static int qeth_l3_start_ipa_tso(struct qeth_card *card) @@ -1445,8 +1339,8 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card) QETH_CARD_IFNAME(card)); rc = -EOPNOTSUPP; } else { - rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_TSO, - IPA_CMD_ASS_START, 0); + rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO, + IPA_CMD_ASS_START, 0); if (rc) dev_warn(&card->gdev->dev, "Starting outbound TCP " "segmentation offload for %s failed\n", @@ -1950,7 +1844,6 @@ static inline int qeth_l3_rebuild_skb(struct qeth_card *card, skb->ip_summed = CHECKSUM_NONE; } else skb->ip_summed = CHECKSUM_NONE; - return is_vlan; } @@ -2287,7 +2180,7 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries) if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING, + rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING, IPA_CMD_ASS_ARP_SET_NO_ENTRIES, no_entries); if (rc) { @@ -2552,7 +2445,7 @@ static int qeth_l3_arp_add_entry(struct qeth_card *card, QETH_PROT_IPV4); if (!iob) return -ENOMEM; - rc = qeth_l3_send_setassparms(card, iob, + rc = qeth_send_setassparms(card, iob, sizeof(struct qeth_arp_cache_entry), (unsigned long) entry, qeth_l3_default_setassparms_cb, NULL); @@ -2593,7 +2486,7 @@ static int qeth_l3_arp_remove_entry(struct qeth_card *card, QETH_PROT_IPV4); if (!iob) return -ENOMEM; - rc = qeth_l3_send_setassparms(card, iob, + rc = qeth_send_setassparms(card, iob, 12, (unsigned long)buf, qeth_l3_default_setassparms_cb, NULL); if (rc) { @@ -2624,7 +2517,7 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card) if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING, + rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING, IPA_CMD_ASS_ARP_FLUSH_CACHE, 0); if (rc) { tmp = rc; @@ -3187,7 +3080,6 @@ static netdev_features_t qeth_l3_fix_features(struct net_device *dev, features &= ~NETIF_F_TSO; if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) features &= ~NETIF_F_RXCSUM; - return features; } @@ -3204,7 +3096,7 @@ static int qeth_l3_set_features(struct net_device *dev, card->state == CARD_STATE_RECOVER) return 0; - return qeth_l3_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0); + return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0); } static const struct ethtool_ops qeth_l3_ethtool_ops = { diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index d8f990b6b332..a851d34c642b 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c @@ -49,7 +49,7 @@ static DEFINE_SPINLOCK(smsg_list_lock); static LIST_HEAD(smsg_list); static int iucv_path_connected; -static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]); +static int smsg_path_pending(struct iucv_path *, u8 *, u8 *); static void smsg_message_pending(struct iucv_path *, struct iucv_message *); static struct iucv_handler smsg_handler = { @@ -57,8 +57,7 @@ static struct iucv_handler smsg_handler = { .message_pending = smsg_message_pending, }; -static int smsg_path_pending(struct iucv_path *path, u8 ipvmid[8], - u8 ipuser[16]) +static int smsg_path_pending(struct iucv_path *path, u8 *ipvmid, u8 *ipuser) { if (strncmp(ipvmid, "*MSG ", 8) != 0) return -EINVAL; diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c index f8d8fdb26b72..e9fae30fafda 100644 --- a/drivers/s390/virtio/virtio_ccw.c +++ b/drivers/s390/virtio/virtio_ccw.c @@ -400,12 +400,16 @@ static bool virtio_ccw_kvm_notify(struct virtqueue *vq) static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, struct ccw1 *ccw, int index) { + int ret; + vcdev->config_block->index = index; ccw->cmd_code = CCW_CMD_READ_VQ_CONF; ccw->flags = 0; ccw->count = sizeof(struct vq_config_block); ccw->cda = (__u32)(unsigned long)(vcdev->config_block); - ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF); + ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF); + if (ret) + return ret; return vcdev->config_block->num; } @@ -503,6 +507,10 @@ static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, goto out_err; } info->num = virtio_ccw_read_vq_conf(vcdev, ccw, i); + if (info->num < 0) { + err = info->num; + goto out_err; + } size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); if (info->queue == NULL) { diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 471d08791766..1a8c9b53fafa 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -172,6 +172,7 @@ scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o scsi_mod-y += scsi_trace.o scsi_logging.o scsi_mod-$(CONFIG_PM) += scsi_pm.o +scsi_mod-$(CONFIG_SCSI_DH) += scsi_dh.o hv_storvsc-y := storvsc_drv.o diff --git a/drivers/scsi/aic94xx/aic94xx_sds.c b/drivers/scsi/aic94xx/aic94xx_sds.c index edb43fda9f36..c831e30411fa 100644 --- a/drivers/scsi/aic94xx/aic94xx_sds.c +++ b/drivers/scsi/aic94xx/aic94xx_sds.c @@ -983,7 +983,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, { int err, i; u32 offs, size; - struct asd_ll_el *el; + struct asd_ll_el *el = NULL; struct asd_ctrla_phy_settings *ps; struct asd_ctrla_phy_settings dflt_ps; @@ -1004,6 +1004,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, size = sizeof(struct asd_ctrla_phy_settings); ps = &dflt_ps; + goto out_process; } if (size == 0) @@ -1028,7 +1029,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, ASD_DPRINTK("couldn't find ctrla phy settings struct\n"); goto out2; } - +out_process: err = asd_process_ctrla_phy_settings(asd_ha, ps); if (err) { ASD_DPRINTK("couldn't process ctrla phy settings\n"); diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c index 315d6d6dcfc8..98f7e8cca52d 100644 --- a/drivers/scsi/bfa/bfa_ioc.c +++ b/drivers/scsi/bfa/bfa_ioc.c @@ -3665,19 +3665,19 @@ bfa_cb_sfp_state_query(struct bfa_sfp_s *sfp) if (sfp->state_query_cbfn) sfp->state_query_cbfn(sfp->state_query_cbarg, sfp->status); - sfp->media = NULL; - } + sfp->media = NULL; + } - if (sfp->portspeed) { - sfp->status = bfa_sfp_speed_valid(sfp, sfp->portspeed); - if (sfp->state_query_cbfn) - sfp->state_query_cbfn(sfp->state_query_cbarg, - sfp->status); - sfp->portspeed = BFA_PORT_SPEED_UNKNOWN; - } + if (sfp->portspeed) { + sfp->status = bfa_sfp_speed_valid(sfp, sfp->portspeed); + if (sfp->state_query_cbfn) + sfp->state_query_cbfn(sfp->state_query_cbarg, + sfp->status); + sfp->portspeed = BFA_PORT_SPEED_UNKNOWN; + } - sfp->state_query_lock = 0; - sfp->state_query_cbfn = NULL; + sfp->state_query_lock = 0; + sfp->state_query_cbfn = NULL; } /* @@ -3878,7 +3878,7 @@ bfa_sfp_show_comp(struct bfa_sfp_s *sfp, struct bfi_mbmsg_s *msg) bfa_trc(sfp, sfp->data_valid); if (sfp->data_valid) { u32 size = sizeof(struct sfp_mem_s); - u8 *des = (u8 *) &(sfp->sfpmem); + u8 *des = (u8 *)(sfp->sfpmem); memcpy(des, sfp->dbuf_kva, size); } /* diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig index 69abd0ad48e2..e5647d59224f 100644 --- a/drivers/scsi/device_handler/Kconfig +++ b/drivers/scsi/device_handler/Kconfig @@ -3,7 +3,7 @@ # menuconfig SCSI_DH - tristate "SCSI Device Handlers" + bool "SCSI Device Handlers" depends on SCSI default n help diff --git a/drivers/scsi/device_handler/Makefile b/drivers/scsi/device_handler/Makefile index e1d2ea083e15..09866c50fbb4 100644 --- a/drivers/scsi/device_handler/Makefile +++ b/drivers/scsi/device_handler/Makefile @@ -1,7 +1,6 @@ # # SCSI Device Handler # -obj-$(CONFIG_SCSI_DH) += scsi_dh.o obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c deleted file mode 100644 index 1efebc9eedfb..000000000000 --- a/drivers/scsi/device_handler/scsi_dh.c +++ /dev/null @@ -1,621 +0,0 @@ -/* - * SCSI device handler infrastruture. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright IBM Corporation, 2007 - * Authors: - * Chandra Seetharaman <sekharan@us.ibm.com> - * Mike Anderson <andmike@linux.vnet.ibm.com> - */ - -#include <linux/slab.h> -#include <linux/module.h> -#include <scsi/scsi_dh.h> -#include "../scsi_priv.h" - -static DEFINE_SPINLOCK(list_lock); -static LIST_HEAD(scsi_dh_list); - -static struct scsi_device_handler *get_device_handler(const char *name) -{ - struct scsi_device_handler *tmp, *found = NULL; - - spin_lock(&list_lock); - list_for_each_entry(tmp, &scsi_dh_list, list) { - if (!strncmp(tmp->name, name, strlen(tmp->name))) { - found = tmp; - break; - } - } - spin_unlock(&list_lock); - return found; -} - -/* - * device_handler_match_function - Match a device handler to a device - * @sdev - SCSI device to be tested - * - * Tests @sdev against the match function of all registered device_handler. - * Returns the found device handler or NULL if not found. - */ -static struct scsi_device_handler * -device_handler_match_function(struct scsi_device *sdev) -{ - struct scsi_device_handler *tmp_dh, *found_dh = NULL; - - spin_lock(&list_lock); - list_for_each_entry(tmp_dh, &scsi_dh_list, list) { - if (tmp_dh->match && tmp_dh->match(sdev)) { - found_dh = tmp_dh; - break; - } - } - spin_unlock(&list_lock); - return found_dh; -} - -/* - * device_handler_match - Attach a device handler to a device - * @scsi_dh - The device handler to match against or NULL - * @sdev - SCSI device to be tested against @scsi_dh - * - * Tests @sdev against the device handler @scsi_dh or against - * all registered device_handler if @scsi_dh == NULL. - * Returns the found device handler or NULL if not found. - */ -static struct scsi_device_handler * -device_handler_match(struct scsi_device_handler *scsi_dh, - struct scsi_device *sdev) -{ - struct scsi_device_handler *found_dh; - - found_dh = device_handler_match_function(sdev); - - if (scsi_dh && found_dh != scsi_dh) - found_dh = NULL; - - return found_dh; -} - -/* - * scsi_dh_handler_attach - Attach a device handler to a device - * @sdev - SCSI device the device handler should attach to - * @scsi_dh - The device handler to attach - */ -static int scsi_dh_handler_attach(struct scsi_device *sdev, - struct scsi_device_handler *scsi_dh) -{ - struct scsi_dh_data *d; - - if (sdev->scsi_dh_data) { - if (sdev->scsi_dh_data->scsi_dh != scsi_dh) - return -EBUSY; - - kref_get(&sdev->scsi_dh_data->kref); - return 0; - } - - if (!try_module_get(scsi_dh->module)) - return -EINVAL; - - d = scsi_dh->attach(sdev); - if (IS_ERR(d)) { - sdev_printk(KERN_ERR, sdev, "%s: Attach failed (%ld)\n", - scsi_dh->name, PTR_ERR(d)); - module_put(scsi_dh->module); - return PTR_ERR(d); - } - - d->scsi_dh = scsi_dh; - kref_init(&d->kref); - d->sdev = sdev; - - spin_lock_irq(sdev->request_queue->queue_lock); - sdev->scsi_dh_data = d; - spin_unlock_irq(sdev->request_queue->queue_lock); - return 0; -} - -static void __detach_handler (struct kref *kref) -{ - struct scsi_dh_data *scsi_dh_data = - container_of(kref, struct scsi_dh_data, kref); - struct scsi_device_handler *scsi_dh = scsi_dh_data->scsi_dh; - struct scsi_device *sdev = scsi_dh_data->sdev; - - scsi_dh->detach(sdev); - - spin_lock_irq(sdev->request_queue->queue_lock); - sdev->scsi_dh_data = NULL; - spin_unlock_irq(sdev->request_queue->queue_lock); - - sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", scsi_dh->name); - module_put(scsi_dh->module); -} - -/* - * scsi_dh_handler_detach - Detach a device handler from a device - * @sdev - SCSI device the device handler should be detached from - * @scsi_dh - Device handler to be detached - * - * Detach from a device handler. If a device handler is specified, - * only detach if the currently attached handler matches @scsi_dh. - */ -static void scsi_dh_handler_detach(struct scsi_device *sdev, - struct scsi_device_handler *scsi_dh) -{ - if (!sdev->scsi_dh_data) - return; - - if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh) - return; - - if (!scsi_dh) - scsi_dh = sdev->scsi_dh_data->scsi_dh; - - if (scsi_dh) - kref_put(&sdev->scsi_dh_data->kref, __detach_handler); -} - -/* - * Functions for sysfs attribute 'dh_state' - */ -static ssize_t -store_dh_state(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct scsi_device_handler *scsi_dh; - int err = -EINVAL; - - if (sdev->sdev_state == SDEV_CANCEL || - sdev->sdev_state == SDEV_DEL) - return -ENODEV; - - if (!sdev->scsi_dh_data) { - /* - * Attach to a device handler - */ - if (!(scsi_dh = get_device_handler(buf))) - return err; - err = scsi_dh_handler_attach(sdev, scsi_dh); - } else { - scsi_dh = sdev->scsi_dh_data->scsi_dh; - if (!strncmp(buf, "detach", 6)) { - /* - * Detach from a device handler - */ - scsi_dh_handler_detach(sdev, scsi_dh); - err = 0; - } else if (!strncmp(buf, "activate", 8)) { - /* - * Activate a device handler - */ - if (scsi_dh->activate) - err = scsi_dh->activate(sdev, NULL, NULL); - else - err = 0; - } - } - - return err<0?err:count; -} - -static ssize_t -show_dh_state(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct scsi_device *sdev = to_scsi_device(dev); - - if (!sdev->scsi_dh_data) - return snprintf(buf, 20, "detached\n"); - - return snprintf(buf, 20, "%s\n", sdev->scsi_dh_data->scsi_dh->name); -} - -static struct device_attribute scsi_dh_state_attr = - __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state, - store_dh_state); - -/* - * scsi_dh_sysfs_attr_add - Callback for scsi_init_dh - */ -static int scsi_dh_sysfs_attr_add(struct device *dev, void *data) -{ - struct scsi_device *sdev; - int err; - - if (!scsi_is_sdev_device(dev)) - return 0; - - sdev = to_scsi_device(dev); - - err = device_create_file(&sdev->sdev_gendev, - &scsi_dh_state_attr); - - return 0; -} - -/* - * scsi_dh_sysfs_attr_remove - Callback for scsi_exit_dh - */ -static int scsi_dh_sysfs_attr_remove(struct device *dev, void *data) -{ - struct scsi_device *sdev; - - if (!scsi_is_sdev_device(dev)) - return 0; - - sdev = to_scsi_device(dev); - - device_remove_file(&sdev->sdev_gendev, - &scsi_dh_state_attr); - - return 0; -} - -/* - * scsi_dh_notifier - notifier chain callback - */ -static int scsi_dh_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct device *dev = data; - struct scsi_device *sdev; - int err = 0; - struct scsi_device_handler *devinfo = NULL; - - if (!scsi_is_sdev_device(dev)) - return 0; - - sdev = to_scsi_device(dev); - - if (action == BUS_NOTIFY_ADD_DEVICE) { - err = device_create_file(dev, &scsi_dh_state_attr); - /* don't care about err */ - devinfo = device_handler_match(NULL, sdev); - if (devinfo) - err = scsi_dh_handler_attach(sdev, devinfo); - } else if (action == BUS_NOTIFY_DEL_DEVICE) { - device_remove_file(dev, &scsi_dh_state_attr); - scsi_dh_handler_detach(sdev, NULL); - } - return err; -} - -/* - * scsi_dh_notifier_add - Callback for scsi_register_device_handler - */ -static int scsi_dh_notifier_add(struct device *dev, void *data) -{ - struct scsi_device_handler *scsi_dh = data; - struct scsi_device *sdev; - - if (!scsi_is_sdev_device(dev)) - return 0; - - if (!get_device(dev)) - return 0; - - sdev = to_scsi_device(dev); - - if (device_handler_match(scsi_dh, sdev)) - scsi_dh_handler_attach(sdev, scsi_dh); - - put_device(dev); - - return 0; -} - -/* - * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler - */ -static int scsi_dh_notifier_remove(struct device *dev, void *data) -{ - struct scsi_device_handler *scsi_dh = data; - struct scsi_device *sdev; - - if (!scsi_is_sdev_device(dev)) - return 0; - - if (!get_device(dev)) - return 0; - - sdev = to_scsi_device(dev); - - scsi_dh_handler_detach(sdev, scsi_dh); - - put_device(dev); - - return 0; -} - -/* - * scsi_register_device_handler - register a device handler personality - * module. - * @scsi_dh - device handler to be registered. - * - * Returns 0 on success, -EBUSY if handler already registered. - */ -int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) -{ - - if (get_device_handler(scsi_dh->name)) - return -EBUSY; - - if (!scsi_dh->attach || !scsi_dh->detach) - return -EINVAL; - - spin_lock(&list_lock); - list_add(&scsi_dh->list, &scsi_dh_list); - spin_unlock(&list_lock); - - bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add); - printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); - - return SCSI_DH_OK; -} -EXPORT_SYMBOL_GPL(scsi_register_device_handler); - -/* - * scsi_unregister_device_handler - register a device handler personality - * module. - * @scsi_dh - device handler to be unregistered. - * - * Returns 0 on success, -ENODEV if handler not registered. - */ -int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) -{ - - if (!get_device_handler(scsi_dh->name)) - return -ENODEV; - - bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, - scsi_dh_notifier_remove); - - spin_lock(&list_lock); - list_del(&scsi_dh->list); - spin_unlock(&list_lock); - printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); - - return SCSI_DH_OK; -} -EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); - -/* - * scsi_dh_activate - activate the path associated with the scsi_device - * corresponding to the given request queue. - * Returns immediately without waiting for activation to be completed. - * @q - Request queue that is associated with the scsi_device to be - * activated. - * @fn - Function to be called upon completion of the activation. - * Function fn is called with data (below) and the error code. - * Function fn may be called from the same calling context. So, - * do not hold the lock in the caller which may be needed in fn. - * @data - data passed to the function fn upon completion. - * - */ -int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) -{ - int err = 0; - unsigned long flags; - struct scsi_device *sdev; - struct scsi_device_handler *scsi_dh = NULL; - struct device *dev = NULL; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (!sdev) { - spin_unlock_irqrestore(q->queue_lock, flags); - err = SCSI_DH_NOSYS; - if (fn) - fn(data, err); - return err; - } - - if (sdev->scsi_dh_data) - scsi_dh = sdev->scsi_dh_data->scsi_dh; - dev = get_device(&sdev->sdev_gendev); - if (!scsi_dh || !dev || - sdev->sdev_state == SDEV_CANCEL || - sdev->sdev_state == SDEV_DEL) - err = SCSI_DH_NOSYS; - if (sdev->sdev_state == SDEV_OFFLINE) - err = SCSI_DH_DEV_OFFLINED; - spin_unlock_irqrestore(q->queue_lock, flags); - - if (err) { - if (fn) - fn(data, err); - goto out; - } - - if (scsi_dh->activate) - err = scsi_dh->activate(sdev, fn, data); -out: - put_device(dev); - return err; -} -EXPORT_SYMBOL_GPL(scsi_dh_activate); - -/* - * scsi_dh_set_params - set the parameters for the device as per the - * string specified in params. - * @q - Request queue that is associated with the scsi_device for - * which the parameters to be set. - * @params - parameters in the following format - * "no_of_params\0param1\0param2\0param3\0...\0" - * for example, string for 2 parameters with value 10 and 21 - * is specified as "2\010\021\0". - */ -int scsi_dh_set_params(struct request_queue *q, const char *params) -{ - int err = -SCSI_DH_NOSYS; - unsigned long flags; - struct scsi_device *sdev; - struct scsi_device_handler *scsi_dh = NULL; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (sdev && sdev->scsi_dh_data) - scsi_dh = sdev->scsi_dh_data->scsi_dh; - if (scsi_dh && scsi_dh->set_params && get_device(&sdev->sdev_gendev)) - err = 0; - spin_unlock_irqrestore(q->queue_lock, flags); - - if (err) - return err; - err = scsi_dh->set_params(sdev, params); - put_device(&sdev->sdev_gendev); - return err; -} -EXPORT_SYMBOL_GPL(scsi_dh_set_params); - -/* - * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for - * the given name. FALSE(0) otherwise. - * @name - name of the device handler. - */ -int scsi_dh_handler_exist(const char *name) -{ - return (get_device_handler(name) != NULL); -} -EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); - -/* - * scsi_dh_attach - Attach device handler - * @q - Request queue that is associated with the scsi_device - * the handler should be attached to - * @name - name of the handler to attach - */ -int scsi_dh_attach(struct request_queue *q, const char *name) -{ - unsigned long flags; - struct scsi_device *sdev; - struct scsi_device_handler *scsi_dh; - int err = 0; - - scsi_dh = get_device_handler(name); - if (!scsi_dh) - return -EINVAL; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (!sdev || !get_device(&sdev->sdev_gendev)) - err = -ENODEV; - spin_unlock_irqrestore(q->queue_lock, flags); - - if (!err) { - err = scsi_dh_handler_attach(sdev, scsi_dh); - put_device(&sdev->sdev_gendev); - } - return err; -} -EXPORT_SYMBOL_GPL(scsi_dh_attach); - -/* - * scsi_dh_detach - Detach device handler - * @q - Request queue that is associated with the scsi_device - * the handler should be detached from - * - * This function will detach the device handler only - * if the sdev is not part of the internal list, ie - * if it has been attached manually. - */ -void scsi_dh_detach(struct request_queue *q) -{ - unsigned long flags; - struct scsi_device *sdev; - struct scsi_device_handler *scsi_dh = NULL; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (!sdev || !get_device(&sdev->sdev_gendev)) - sdev = NULL; - spin_unlock_irqrestore(q->queue_lock, flags); - - if (!sdev) - return; - - if (sdev->scsi_dh_data) { - scsi_dh = sdev->scsi_dh_data->scsi_dh; - scsi_dh_handler_detach(sdev, scsi_dh); - } - put_device(&sdev->sdev_gendev); -} -EXPORT_SYMBOL_GPL(scsi_dh_detach); - -/* - * scsi_dh_attached_handler_name - Get attached device handler's name - * @q - Request queue that is associated with the scsi_device - * that may have a device handler attached - * @gfp - the GFP mask used in the kmalloc() call when allocating memory - * - * Returns name of attached handler, NULL if no handler is attached. - * Caller must take care to free the returned string. - */ -const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) -{ - unsigned long flags; - struct scsi_device *sdev; - const char *handler_name = NULL; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (!sdev || !get_device(&sdev->sdev_gendev)) - sdev = NULL; - spin_unlock_irqrestore(q->queue_lock, flags); - - if (!sdev) - return NULL; - - if (sdev->scsi_dh_data) - handler_name = kstrdup(sdev->scsi_dh_data->scsi_dh->name, gfp); - - put_device(&sdev->sdev_gendev); - return handler_name; -} -EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name); - -static struct notifier_block scsi_dh_nb = { - .notifier_call = scsi_dh_notifier -}; - -static int __init scsi_dh_init(void) -{ - int r; - - r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb); - - if (!r) - bus_for_each_dev(&scsi_bus_type, NULL, NULL, - scsi_dh_sysfs_attr_add); - - return r; -} - -static void __exit scsi_dh_exit(void) -{ - bus_for_each_dev(&scsi_bus_type, NULL, NULL, - scsi_dh_sysfs_attr_remove); - bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb); -} - -module_init(scsi_dh_init); -module_exit(scsi_dh_exit); - -MODULE_DESCRIPTION("SCSI device handler"); -MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 854b568b9931..cc2773b5de68 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -62,7 +62,6 @@ #define ALUA_OPTIMIZE_STPG 1 struct alua_dh_data { - struct scsi_dh_data dh_data; int group_id; int rel_port; int tpgs; @@ -86,11 +85,6 @@ struct alua_dh_data { static char print_alua_state(int); static int alua_check_sense(struct scsi_device *, struct scsi_sense_hdr *); -static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev) -{ - return container_of(sdev->scsi_dh_data, struct alua_dh_data, dh_data); -} - static int realloc_buffer(struct alua_dh_data *h, unsigned len) { if (h->buff && h->buff != h->inq) @@ -708,7 +702,7 @@ out: */ static int alua_set_params(struct scsi_device *sdev, const char *params) { - struct alua_dh_data *h = get_alua_data(sdev); + struct alua_dh_data *h = sdev->handler_data; unsigned int optimize = 0, argc; const char *p = params; int result = SCSI_DH_OK; @@ -746,7 +740,7 @@ MODULE_PARM_DESC(optimize_stpg, "Allow use of a non-optimized path, rather than static int alua_activate(struct scsi_device *sdev, activate_complete fn, void *data) { - struct alua_dh_data *h = get_alua_data(sdev); + struct alua_dh_data *h = sdev->handler_data; int err = SCSI_DH_OK; int stpg = 0; @@ -804,7 +798,7 @@ out: */ static int alua_prep_fn(struct scsi_device *sdev, struct request *req) { - struct alua_dh_data *h = get_alua_data(sdev); + struct alua_dh_data *h = sdev->handler_data; int ret = BLKPREP_OK; if (h->state == TPGS_STATE_TRANSITIONING) @@ -819,23 +813,18 @@ static int alua_prep_fn(struct scsi_device *sdev, struct request *req) } -static bool alua_match(struct scsi_device *sdev) -{ - return (scsi_device_tpgs(sdev) != 0); -} - /* * alua_bus_attach - Attach device handler * @sdev: device to be attached to */ -static struct scsi_dh_data *alua_bus_attach(struct scsi_device *sdev) +static int alua_bus_attach(struct scsi_device *sdev) { struct alua_dh_data *h; int err; h = kzalloc(sizeof(*h) , GFP_KERNEL); if (!h) - return ERR_PTR(-ENOMEM); + return -ENOMEM; h->tpgs = TPGS_MODE_UNINITIALIZED; h->state = TPGS_STATE_OPTIMIZED; h->group_id = -1; @@ -848,11 +837,11 @@ static struct scsi_dh_data *alua_bus_attach(struct scsi_device *sdev) if (err != SCSI_DH_OK && err != SCSI_DH_DEV_OFFLINED) goto failed; - sdev_printk(KERN_NOTICE, sdev, "%s: Attached\n", ALUA_DH_NAME); - return &h->dh_data; + sdev->handler_data = h; + return 0; failed: kfree(h); - return ERR_PTR(-EINVAL); + return -EINVAL; } /* @@ -861,10 +850,11 @@ failed: */ static void alua_bus_detach(struct scsi_device *sdev) { - struct alua_dh_data *h = get_alua_data(sdev); + struct alua_dh_data *h = sdev->handler_data; if (h->buff && h->inq != h->buff) kfree(h->buff); + sdev->handler_data = NULL; kfree(h); } @@ -877,7 +867,6 @@ static struct scsi_device_handler alua_dh = { .check_sense = alua_check_sense, .activate = alua_activate, .set_params = alua_set_params, - .match = alua_match, }; static int __init alua_init(void) diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index 6ed1caadbc6a..e6fb97cb12f4 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -72,7 +72,6 @@ static const char * lun_state[] = }; struct clariion_dh_data { - struct scsi_dh_data dh_data; /* * Flags: * CLARIION_SHORT_TRESPASS @@ -114,13 +113,6 @@ struct clariion_dh_data { int current_sp; }; -static inline struct clariion_dh_data - *get_clariion_data(struct scsi_device *sdev) -{ - return container_of(sdev->scsi_dh_data, struct clariion_dh_data, - dh_data); -} - /* * Parse MODE_SELECT cmd reply. */ @@ -450,7 +442,7 @@ static int clariion_check_sense(struct scsi_device *sdev, static int clariion_prep_fn(struct scsi_device *sdev, struct request *req) { - struct clariion_dh_data *h = get_clariion_data(sdev); + struct clariion_dh_data *h = sdev->handler_data; int ret = BLKPREP_OK; if (h->lun_state != CLARIION_LUN_OWNED) { @@ -533,7 +525,7 @@ retry: static int clariion_activate(struct scsi_device *sdev, activate_complete fn, void *data) { - struct clariion_dh_data *csdev = get_clariion_data(sdev); + struct clariion_dh_data *csdev = sdev->handler_data; int result; result = clariion_send_inquiry(sdev, csdev); @@ -574,7 +566,7 @@ done: */ static int clariion_set_params(struct scsi_device *sdev, const char *params) { - struct clariion_dh_data *csdev = get_clariion_data(sdev); + struct clariion_dh_data *csdev = sdev->handler_data; unsigned int hr = 0, st = 0, argc; const char *p = params; int result = SCSI_DH_OK; @@ -622,42 +614,14 @@ done: return result; } -static const struct { - char *vendor; - char *model; -} clariion_dev_list[] = { - {"DGC", "RAID"}, - {"DGC", "DISK"}, - {"DGC", "VRAID"}, - {NULL, NULL}, -}; - -static bool clariion_match(struct scsi_device *sdev) -{ - int i; - - if (scsi_device_tpgs(sdev)) - return false; - - for (i = 0; clariion_dev_list[i].vendor; i++) { - if (!strncmp(sdev->vendor, clariion_dev_list[i].vendor, - strlen(clariion_dev_list[i].vendor)) && - !strncmp(sdev->model, clariion_dev_list[i].model, - strlen(clariion_dev_list[i].model))) { - return true; - } - } - return false; -} - -static struct scsi_dh_data *clariion_bus_attach(struct scsi_device *sdev) +static int clariion_bus_attach(struct scsi_device *sdev) { struct clariion_dh_data *h; int err; h = kzalloc(sizeof(*h) , GFP_KERNEL); if (!h) - return ERR_PTR(-ENOMEM); + return -ENOMEM; h->lun_state = CLARIION_LUN_UNINITIALIZED; h->default_sp = CLARIION_UNBOUND_LU; h->current_sp = CLARIION_UNBOUND_LU; @@ -675,18 +639,19 @@ static struct scsi_dh_data *clariion_bus_attach(struct scsi_device *sdev) CLARIION_NAME, h->current_sp + 'A', h->port, lun_state[h->lun_state], h->default_sp + 'A'); - return &h->dh_data; + + sdev->handler_data = h; + return 0; failed: kfree(h); - return ERR_PTR(-EINVAL); + return -EINVAL; } static void clariion_bus_detach(struct scsi_device *sdev) { - struct clariion_dh_data *h = get_clariion_data(sdev); - - kfree(h); + kfree(sdev->handler_data); + sdev->handler_data = NULL; } static struct scsi_device_handler clariion_dh = { @@ -698,7 +663,6 @@ static struct scsi_device_handler clariion_dh = { .activate = clariion_activate, .prep_fn = clariion_prep_fn, .set_params = clariion_set_params, - .match = clariion_match, }; static int __init clariion_init(void) diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index 485d99544a15..9406d5f4a3d3 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -38,7 +38,6 @@ #define HP_SW_PATH_PASSIVE 1 struct hp_sw_dh_data { - struct scsi_dh_data dh_data; unsigned char sense[SCSI_SENSE_BUFFERSIZE]; int path_state; int retries; @@ -50,11 +49,6 @@ struct hp_sw_dh_data { static int hp_sw_start_stop(struct hp_sw_dh_data *); -static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev) -{ - return container_of(sdev->scsi_dh_data, struct hp_sw_dh_data, dh_data); -} - /* * tur_done - Handle TEST UNIT READY return status * @sdev: sdev the command has been sent to @@ -267,7 +261,7 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h) static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) { - struct hp_sw_dh_data *h = get_hp_sw_data(sdev); + struct hp_sw_dh_data *h = sdev->handler_data; int ret = BLKPREP_OK; if (h->path_state != HP_SW_PATH_ACTIVE) { @@ -292,7 +286,7 @@ static int hp_sw_activate(struct scsi_device *sdev, activate_complete fn, void *data) { int ret = SCSI_DH_OK; - struct hp_sw_dh_data *h = get_hp_sw_data(sdev); + struct hp_sw_dh_data *h = sdev->handler_data; ret = hp_sw_tur(sdev, h); @@ -311,43 +305,14 @@ static int hp_sw_activate(struct scsi_device *sdev, return 0; } -static const struct { - char *vendor; - char *model; -} hp_sw_dh_data_list[] = { - {"COMPAQ", "MSA1000 VOLUME"}, - {"COMPAQ", "HSV110"}, - {"HP", "HSV100"}, - {"DEC", "HSG80"}, - {NULL, NULL}, -}; - -static bool hp_sw_match(struct scsi_device *sdev) -{ - int i; - - if (scsi_device_tpgs(sdev)) - return false; - - for (i = 0; hp_sw_dh_data_list[i].vendor; i++) { - if (!strncmp(sdev->vendor, hp_sw_dh_data_list[i].vendor, - strlen(hp_sw_dh_data_list[i].vendor)) && - !strncmp(sdev->model, hp_sw_dh_data_list[i].model, - strlen(hp_sw_dh_data_list[i].model))) { - return true; - } - } - return false; -} - -static struct scsi_dh_data *hp_sw_bus_attach(struct scsi_device *sdev) +static int hp_sw_bus_attach(struct scsi_device *sdev) { struct hp_sw_dh_data *h; int ret; h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) - return ERR_PTR(-ENOMEM); + return -ENOMEM; h->path_state = HP_SW_PATH_UNINITIALIZED; h->retries = HP_SW_RETRIES; h->sdev = sdev; @@ -359,17 +324,18 @@ static struct scsi_dh_data *hp_sw_bus_attach(struct scsi_device *sdev) sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n", HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE? "active":"passive"); - return &h->dh_data; + + sdev->handler_data = h; + return 0; failed: kfree(h); - return ERR_PTR(-EINVAL); + return -EINVAL; } static void hp_sw_bus_detach( struct scsi_device *sdev ) { - struct hp_sw_dh_data *h = get_hp_sw_data(sdev); - - kfree(h); + kfree(sdev->handler_data); + sdev->handler_data = NULL; } static struct scsi_device_handler hp_sw_dh = { @@ -379,7 +345,6 @@ static struct scsi_device_handler hp_sw_dh = { .detach = hp_sw_bus_detach, .activate = hp_sw_activate, .prep_fn = hp_sw_prep_fn, - .match = hp_sw_match, }; static int __init hp_sw_init(void) diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index b46ace3d4bf0..361358134315 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -181,7 +181,6 @@ struct c2_inquiry { }; struct rdac_dh_data { - struct scsi_dh_data dh_data; struct rdac_controller *ctlr; #define UNINITIALIZED_LUN (1 << 8) unsigned lun; @@ -260,11 +259,6 @@ do { \ sdev_printk(KERN_INFO, sdev, RDAC_NAME ": " f "\n", ## arg); \ } while (0); -static inline struct rdac_dh_data *get_rdac_data(struct scsi_device *sdev) -{ - return container_of(sdev->scsi_dh_data, struct rdac_dh_data, dh_data); -} - static struct request *get_rdac_req(struct scsi_device *sdev, void *buffer, unsigned buflen, int rw) { @@ -544,7 +538,7 @@ static int mode_select_handle_sense(struct scsi_device *sdev, { struct scsi_sense_hdr sense_hdr; int err = SCSI_DH_IO, ret; - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; ret = scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sense_hdr); if (!ret) @@ -589,7 +583,7 @@ static void send_mode_select(struct work_struct *work) container_of(work, struct rdac_controller, ms_work); struct request *rq; struct scsi_device *sdev = ctlr->ms_sdev; - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; struct request_queue *q = sdev->request_queue; int err, retry_cnt = RDAC_RETRY_COUNT; struct rdac_queue_data *tmp, *qdata; @@ -648,7 +642,7 @@ static int queue_mode_select(struct scsi_device *sdev, if (!qdata) return SCSI_DH_RETRY; - qdata->h = get_rdac_data(sdev); + qdata->h = sdev->handler_data; qdata->callback_fn = fn; qdata->callback_data = data; @@ -667,7 +661,7 @@ static int queue_mode_select(struct scsi_device *sdev, static int rdac_activate(struct scsi_device *sdev, activate_complete fn, void *data) { - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; int err = SCSI_DH_OK; int act = 0; @@ -702,7 +696,7 @@ done: static int rdac_prep_fn(struct scsi_device *sdev, struct request *req) { - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; int ret = BLKPREP_OK; if (h->state != RDAC_STATE_ACTIVE) { @@ -716,7 +710,7 @@ static int rdac_prep_fn(struct scsi_device *sdev, struct request *req) static int rdac_check_sense(struct scsi_device *sdev, struct scsi_sense_hdr *sense_hdr) { - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; RDAC_LOG(RDAC_LOG_SENSE, sdev, "array %s, ctlr %d, " "I/O returned with sense %02x/%02x/%02x", @@ -778,56 +772,7 @@ static int rdac_check_sense(struct scsi_device *sdev, return SCSI_RETURN_NOT_HANDLED; } -static const struct { - char *vendor; - char *model; -} rdac_dev_list[] = { - {"IBM", "1722"}, - {"IBM", "1724"}, - {"IBM", "1726"}, - {"IBM", "1742"}, - {"IBM", "1745"}, - {"IBM", "1746"}, - {"IBM", "1813"}, - {"IBM", "1814"}, - {"IBM", "1815"}, - {"IBM", "1818"}, - {"IBM", "3526"}, - {"SGI", "TP9"}, - {"SGI", "IS"}, - {"STK", "OPENstorage D280"}, - {"STK", "FLEXLINE 380"}, - {"SUN", "CSM"}, - {"SUN", "LCSM100"}, - {"SUN", "STK6580_6780"}, - {"SUN", "SUN_6180"}, - {"SUN", "ArrayStorage"}, - {"DELL", "MD3"}, - {"NETAPP", "INF-01-00"}, - {"LSI", "INF-01-00"}, - {"ENGENIO", "INF-01-00"}, - {NULL, NULL}, -}; - -static bool rdac_match(struct scsi_device *sdev) -{ - int i; - - if (scsi_device_tpgs(sdev)) - return false; - - for (i = 0; rdac_dev_list[i].vendor; i++) { - if (!strncmp(sdev->vendor, rdac_dev_list[i].vendor, - strlen(rdac_dev_list[i].vendor)) && - !strncmp(sdev->model, rdac_dev_list[i].model, - strlen(rdac_dev_list[i].model))) { - return true; - } - } - return false; -} - -static struct scsi_dh_data *rdac_bus_attach(struct scsi_device *sdev) +static int rdac_bus_attach(struct scsi_device *sdev) { struct rdac_dh_data *h; int err; @@ -836,7 +781,7 @@ static struct scsi_dh_data *rdac_bus_attach(struct scsi_device *sdev) h = kzalloc(sizeof(*h) , GFP_KERNEL); if (!h) - return ERR_PTR(-ENOMEM); + return -ENOMEM; h->lun = UNINITIALIZED_LUN; h->state = RDAC_STATE_ACTIVE; @@ -861,7 +806,8 @@ static struct scsi_dh_data *rdac_bus_attach(struct scsi_device *sdev) RDAC_NAME, h->lun, mode[(int)h->mode], lun_state[(int)h->lun_state]); - return &h->dh_data; + sdev->handler_data = h; + return 0; clean_ctlr: spin_lock(&list_lock); @@ -870,12 +816,12 @@ clean_ctlr: failed: kfree(h); - return ERR_PTR(-EINVAL); + return -EINVAL; } static void rdac_bus_detach( struct scsi_device *sdev ) { - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; if (h->ctlr && h->ctlr->ms_queued) flush_workqueue(kmpath_rdacd); @@ -884,6 +830,7 @@ static void rdac_bus_detach( struct scsi_device *sdev ) if (h->ctlr) kref_put(&h->ctlr->kref, release_controller); spin_unlock(&list_lock); + sdev->handler_data = NULL; kfree(h); } @@ -895,7 +842,6 @@ static struct scsi_device_handler rdac_dh = { .attach = rdac_bus_attach, .detach = rdac_bus_detach, .activate = rdac_activate, - .match = rdac_match, }; static int __init rdac_init(void) diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index ec193a8357d7..d3eb80c46bbe 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -364,7 +364,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, * on the ethertype for the given device */ fcoe->fcoe_packet_type.func = fcoe_rcv; - fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); + fcoe->fcoe_packet_type.type = htons(ETH_P_FCOE); fcoe->fcoe_packet_type.dev = netdev; dev_add_pack(&fcoe->fcoe_packet_type); diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 341191952155..b62836ddbbee 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4555,7 +4555,7 @@ static ssize_t ipr_store_raw_mode(struct device *dev, spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); res = (struct ipr_resource_entry *)sdev->hostdata; if (res) { - if (ioa_cfg->sis64 && ipr_is_af_dasd_device(res)) { + if (ipr_is_af_dasd_device(res)) { res->raw_mode = simple_strtoul(buf, NULL, 10); len = strlen(buf); if (res->sdev) @@ -6383,9 +6383,13 @@ static int ipr_queuecommand(struct Scsi_Host *shost, (!ipr_is_gscsi(res) || scsi_cmd->cmnd[0] == IPR_QUERY_RSRC_STATE)) { ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD; } - if (res->raw_mode && ipr_is_af_dasd_device(res)) + if (res->raw_mode && ipr_is_af_dasd_device(res)) { ioarcb->cmd_pkt.request_type = IPR_RQTYPE_PIPE; + if (scsi_cmd->underflow == 0) + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK; + } + if (ioa_cfg->sis64) rc = ipr_build_ioadl64(ioa_cfg, ipr_cmd); else diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 98d9bb6ff725..33c74d3436c9 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -853,12 +853,9 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, SAM_STAT_CHECK_CONDITION; scsi_build_sense_buffer(1, sc->sense_buffer, ILLEGAL_REQUEST, 0x10, ascq); - sc->sense_buffer[7] = 0xc; /* Additional sense length */ - sc->sense_buffer[8] = 0; /* Information desc type */ - sc->sense_buffer[9] = 0xa; /* Additional desc length */ - sc->sense_buffer[10] = 0x80; /* Validity bit */ - - put_unaligned_be64(sector, &sc->sense_buffer[12]); + scsi_set_sense_information(sc->sense_buffer, + SCSI_SENSE_BUFFERSIZE, + sector); goto out; } } diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index eb627724417e..4abb93a83e0f 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -2284,7 +2284,7 @@ lpfc_mbx_cmpl_rdp_page_a2(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox) (struct lpfc_rdp_context *)(mbox->context2); if (bf_get(lpfc_mqe_status, &mbox->u.mqe)) - goto error; + goto error_mbuf_free; lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a2, DMP_SFF_PAGE_A2_SIZE); @@ -2299,13 +2299,14 @@ lpfc_mbx_cmpl_rdp_page_a2(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox) mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_link_stat; mbox->context2 = (struct lpfc_rdp_context *) rdp_context; if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) == MBX_NOT_FINISHED) - goto error; + goto error_cmd_free; return; -error: +error_mbuf_free: lpfc_mbuf_free(phba, mp->virt, mp->phys); kfree(mp); +error_cmd_free: lpfc_sli4_mbox_cmd_free(phba, mbox); rdp_context->cmpl(phba, rdp_context, FAILURE); } diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 6dec7cff316f..c167911221e9 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -112,9 +112,12 @@ _scsih_set_fwfault_debug(const char *val, struct kernel_param *kp) if (ret) return ret; + /* global ioc spinlock to protect controller list on list operations */ printk(KERN_INFO "setting fwfault_debug(%d)\n", mpt2sas_fwfault_debug); + spin_lock(&gioc_lock); list_for_each_entry(ioc, &mpt2sas_ioc_list, list) ioc->fwfault_debug = mpt2sas_fwfault_debug; + spin_unlock(&gioc_lock); return 0; } @@ -4437,6 +4440,8 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc) dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); + /* synchronizing freeing resource with pci_access_mutex lock */ + mutex_lock(&ioc->pci_access_mutex); if (ioc->chip_phys && ioc->chip) { _base_mask_interrupts(ioc); ioc->shost_recovery = 1; @@ -4456,6 +4461,7 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc) pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); } + mutex_unlock(&ioc->pci_access_mutex); return; } diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index caff8d10cca4..97ea360c6920 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -238,6 +238,7 @@ * @flags: MPT_TARGET_FLAGS_XXX flags * @deleted: target flaged for deletion * @tm_busy: target is busy with TM request. + * @sdev: The sas_device associated with this target */ struct MPT2SAS_TARGET { struct scsi_target *starget; @@ -248,6 +249,7 @@ struct MPT2SAS_TARGET { u32 flags; u8 deleted; u8 tm_busy; + struct _sas_device *sdev; }; @@ -376,8 +378,24 @@ struct _sas_device { u8 phy; u8 responding; u8 pfa_led_on; + struct kref refcount; }; +static inline void sas_device_get(struct _sas_device *s) +{ + kref_get(&s->refcount); +} + +static inline void sas_device_free(struct kref *r) +{ + kfree(container_of(r, struct _sas_device, refcount)); +} + +static inline void sas_device_put(struct _sas_device *s) +{ + kref_put(&s->refcount, sas_device_free); +} + /** * struct _raid_device - raid volume link list * @list: sas device list @@ -799,6 +817,12 @@ typedef void (*MPT2SAS_FLUSH_RUNNING_CMDS)(struct MPT2SAS_ADAPTER *ioc); * @delayed_tr_list: target reset link list * @delayed_tr_volume_list: volume target reset link list * @@temp_sensors_count: flag to carry the number of temperature sensors + * @pci_access_mutex: Mutex to synchronize ioctl,sysfs show path and + * pci resource handling. PCI resource freeing will lead to free + * vital hardware/memory resource, which might be in use by cli/sysfs + * path functions resulting in Null pointer reference followed by kernel + * crash. To avoid the above race condition we use mutex syncrhonization + * which ensures the syncrhonization between cli/sysfs_show path */ struct MPT2SAS_ADAPTER { struct list_head list; @@ -1015,6 +1039,7 @@ struct MPT2SAS_ADAPTER { u8 mfg_pg10_hide_flag; u8 hide_drives; + struct mutex pci_access_mutex; }; typedef u8 (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, @@ -1023,6 +1048,17 @@ typedef u8 (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, /* base shared API */ extern struct list_head mpt2sas_ioc_list; +/* spinlock on list operations over IOCs + * Case: when multiple warpdrive cards(IOCs) are in use + * Each IOC will added to the ioc list stucture on initialization. + * Watchdog threads run at regular intervals to check IOC for any + * fault conditions which will trigger the dead_ioc thread to + * deallocate pci resource, resulting deleting the IOC netry from list, + * this deletion need to protected by spinlock to enusre that + * ioc removal is syncrhonized, if not synchronized it might lead to + * list_del corruption as the ioc list is traversed in cli path + */ +extern spinlock_t gioc_lock; void mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc); void mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc); @@ -1095,11 +1131,12 @@ struct _sas_node *mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER * u16 handle); struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, u64 sas_address); -struct _sas_device *mpt2sas_scsih_sas_device_find_by_sas_address( +struct _sas_device *mpt2sas_get_sdev_by_addr( + struct MPT2SAS_ADAPTER *ioc, u64 sas_address); +struct _sas_device *__mpt2sas_get_sdev_by_addr( struct MPT2SAS_ADAPTER *ioc, u64 sas_address); void mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc); - void mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); /* config shared API */ diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index 4e509604b571..3694b63bd993 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -427,13 +427,16 @@ static int _ctl_verify_adapter(int ioc_number, struct MPT2SAS_ADAPTER **iocpp) { struct MPT2SAS_ADAPTER *ioc; - + /* global ioc lock to protect controller on list operations */ + spin_lock(&gioc_lock); list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { if (ioc->id != ioc_number) continue; + spin_unlock(&gioc_lock); *iocpp = ioc; return ioc_number; } + spin_unlock(&gioc_lock); *iocpp = NULL; return -1; } @@ -522,10 +525,15 @@ _ctl_poll(struct file *filep, poll_table *wait) poll_wait(filep, &ctl_poll_wait, wait); + /* global ioc lock to protect controller on list operations */ + spin_lock(&gioc_lock); list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { - if (ioc->aen_event_read_flag) + if (ioc->aen_event_read_flag) { + spin_unlock(&gioc_lock); return POLLIN | POLLRDNORM; + } } + spin_unlock(&gioc_lock); return 0; } @@ -2168,16 +2176,23 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, if (_ctl_verify_adapter(ioctl_header.ioc_number, &ioc) == -1 || !ioc) return -ENODEV; + /* pci_access_mutex lock acquired by ioctl path */ + mutex_lock(&ioc->pci_access_mutex); if (ioc->shost_recovery || ioc->pci_error_recovery || - ioc->is_driver_loading) - return -EAGAIN; + ioc->is_driver_loading || ioc->remove_host) { + ret = -EAGAIN; + goto out_unlock_pciaccess; + } state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING; if (state == NON_BLOCKING) { - if (!mutex_trylock(&ioc->ctl_cmds.mutex)) - return -EAGAIN; + if (!mutex_trylock(&ioc->ctl_cmds.mutex)) { + ret = -EAGAIN; + goto out_unlock_pciaccess; + } } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) { - return -ERESTARTSYS; + ret = -ERESTARTSYS; + goto out_unlock_pciaccess; } switch (cmd) { @@ -2258,6 +2273,8 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, } mutex_unlock(&ioc->ctl_cmds.mutex); +out_unlock_pciaccess: + mutex_unlock(&ioc->pci_access_mutex); return ret; } @@ -2711,6 +2728,12 @@ _ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, "warpdrive\n", ioc->name, __func__); goto out; } + /* pci_access_mutex lock acquired by sysfs show path */ + mutex_lock(&ioc->pci_access_mutex); + if (ioc->pci_error_recovery || ioc->remove_host) { + mutex_unlock(&ioc->pci_access_mutex); + return 0; + } /* allocate upto GPIOVal 36 entries */ sz = offsetof(Mpi2IOUnitPage3_t, GPIOVal) + (sizeof(u16) * 36); @@ -2749,6 +2772,7 @@ _ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, out: kfree(io_unit_pg3); + mutex_unlock(&ioc->pci_access_mutex); return rc; } static DEVICE_ATTR(BRM_status, S_IRUGO, _ctl_BRM_status_show, NULL); diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 3f26147bbc64..0ad09b2bff9c 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -79,7 +79,8 @@ static int _scsih_scan_finished(struct Scsi_Host *shost, unsigned long time); /* global parameters */ LIST_HEAD(mpt2sas_ioc_list); - +/* global ioc lock for list operations */ +DEFINE_SPINLOCK(gioc_lock); /* local parameters */ static u8 scsi_io_cb_idx = -1; static u8 tm_cb_idx = -1; @@ -176,9 +177,37 @@ struct fw_event_work { u8 VP_ID; u8 ignore; u16 event; + struct kref refcount; char event_data[0] __aligned(4); }; +static void fw_event_work_free(struct kref *r) +{ + kfree(container_of(r, struct fw_event_work, refcount)); +} + +static void fw_event_work_get(struct fw_event_work *fw_work) +{ + kref_get(&fw_work->refcount); +} + +static void fw_event_work_put(struct fw_event_work *fw_work) +{ + kref_put(&fw_work->refcount, fw_event_work_free); +} + +static struct fw_event_work *alloc_fw_event_work(int len) +{ + struct fw_event_work *fw_event; + + fw_event = kzalloc(sizeof(*fw_event) + len, GFP_ATOMIC); + if (!fw_event) + return NULL; + + kref_init(&fw_event->refcount); + return fw_event; +} + /* raid transport support */ static struct raid_template *mpt2sas_raid_template; @@ -293,8 +322,10 @@ _scsih_set_debug_level(const char *val, struct kernel_param *kp) return ret; printk(KERN_INFO "setting logging_level(0x%08x)\n", logging_level); + spin_lock(&gioc_lock); list_for_each_entry(ioc, &mpt2sas_ioc_list, list) ioc->logging_level = logging_level; + spin_unlock(&gioc_lock); return 0; } module_param_call(logging_level, _scsih_set_debug_level, param_get_int, @@ -526,8 +557,61 @@ _scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc, } } +static struct _sas_device * +__mpt2sas_get_sdev_from_target(struct MPT2SAS_ADAPTER *ioc, + struct MPT2SAS_TARGET *tgt_priv) +{ + struct _sas_device *ret; + + assert_spin_locked(&ioc->sas_device_lock); + + ret = tgt_priv->sdev; + if (ret) + sas_device_get(ret); + + return ret; +} + +static struct _sas_device * +mpt2sas_get_sdev_from_target(struct MPT2SAS_ADAPTER *ioc, + struct MPT2SAS_TARGET *tgt_priv) +{ + struct _sas_device *ret; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + ret = __mpt2sas_get_sdev_from_target(ioc, tgt_priv); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + return ret; +} + + +struct _sas_device * +__mpt2sas_get_sdev_by_addr(struct MPT2SAS_ADAPTER *ioc, + u64 sas_address) +{ + struct _sas_device *sas_device; + + assert_spin_locked(&ioc->sas_device_lock); + + list_for_each_entry(sas_device, &ioc->sas_device_list, list) + if (sas_device->sas_address == sas_address) + goto found_device; + + list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) + if (sas_device->sas_address == sas_address) + goto found_device; + + return NULL; + +found_device: + sas_device_get(sas_device); + return sas_device; +} + /** - * mpt2sas_scsih_sas_device_find_by_sas_address - sas device search + * mpt2sas_get_sdev_by_addr - sas device search * @ioc: per adapter object * @sas_address: sas address * Context: Calling function should acquire ioc->sas_device_lock @@ -536,24 +620,44 @@ _scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc, * object. */ struct _sas_device * -mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, +mpt2sas_get_sdev_by_addr(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) { struct _sas_device *sas_device; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = __mpt2sas_get_sdev_by_addr(ioc, + sas_address); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + return sas_device; +} + +static struct _sas_device * +__mpt2sas_get_sdev_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct _sas_device *sas_device; + + assert_spin_locked(&ioc->sas_device_lock); list_for_each_entry(sas_device, &ioc->sas_device_list, list) - if (sas_device->sas_address == sas_address) - return sas_device; + if (sas_device->handle == handle) + goto found_device; list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) - if (sas_device->sas_address == sas_address) - return sas_device; + if (sas_device->handle == handle) + goto found_device; return NULL; + +found_device: + sas_device_get(sas_device); + return sas_device; } /** - * _scsih_sas_device_find_by_handle - sas device search + * mpt2sas_get_sdev_by_handle - sas device search * @ioc: per adapter object * @handle: sas device handle (assigned by firmware) * Context: Calling function should acquire ioc->sas_device_lock @@ -562,19 +666,16 @@ mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, * object. */ static struct _sas_device * -_scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) +mpt2sas_get_sdev_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) { struct _sas_device *sas_device; + unsigned long flags; - list_for_each_entry(sas_device, &ioc->sas_device_list, list) - if (sas_device->handle == handle) - return sas_device; - - list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) - if (sas_device->handle == handle) - return sas_device; + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return NULL; + return sas_device; } /** @@ -583,7 +684,7 @@ _scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) * @sas_device: the sas_device object * Context: This function will acquire ioc->sas_device_lock. * - * Removing object and freeing associated memory from the ioc->sas_device_list. + * If sas_device is on the list, remove it and decrement its reference count. */ static void _scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc, @@ -594,9 +695,15 @@ _scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc, if (!sas_device) return; + /* + * The lock serializes access to the list, but we still need to verify + * that nobody removed the entry while we were waiting on the lock. + */ spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_del(&sas_device->list); - kfree(sas_device); + if (!list_empty(&sas_device->list)) { + list_del_init(&sas_device->list); + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } @@ -620,6 +727,7 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc, sas_device->handle, (unsigned long long)sas_device->sas_address)); spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device_get(sas_device); list_add_tail(&sas_device->list, &ioc->sas_device_list); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -659,6 +767,7 @@ _scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc, sas_device->handle, (unsigned long long)sas_device->sas_address)); spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device_get(sas_device); list_add_tail(&sas_device->list, &ioc->sas_device_init_list); _scsih_determine_boot_device(ioc, sas_device, 0); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -1208,12 +1317,15 @@ _scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) goto not_sata; if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) goto not_sata; + spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_device_priv_data->sas_target->sas_address); - if (sas_device && sas_device->device_info & - MPI2_SAS_DEVICE_INFO_SATA_DEVICE) - max_depth = MPT2SAS_SATA_QUEUE_DEPTH; + sas_device = __mpt2sas_get_sdev_from_target(ioc, sas_target_priv_data); + if (sas_device) { + if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) + max_depth = MPT2SAS_SATA_QUEUE_DEPTH; + + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); not_sata: @@ -1271,18 +1383,20 @@ _scsih_target_alloc(struct scsi_target *starget) /* sas/sata devices */ spin_lock_irqsave(&ioc->sas_device_lock, flags); rphy = dev_to_rphy(starget->dev.parent); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt2sas_get_sdev_by_addr(ioc, rphy->identify.sas_address); if (sas_device) { sas_target_priv_data->handle = sas_device->handle; sas_target_priv_data->sas_address = sas_device->sas_address; + sas_target_priv_data->sdev = sas_device; sas_device->starget = starget; sas_device->id = starget->id; sas_device->channel = starget->channel; if (test_bit(sas_device->handle, ioc->pd_handles)) sas_target_priv_data->flags |= MPT_TARGET_FLAGS_RAID_COMPONENT; + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -1324,13 +1438,21 @@ _scsih_target_destroy(struct scsi_target *starget) spin_lock_irqsave(&ioc->sas_device_lock, flags); rphy = dev_to_rphy(starget->dev.parent); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - rphy->identify.sas_address); + sas_device = __mpt2sas_get_sdev_from_target(ioc, sas_target_priv_data); if (sas_device && (sas_device->starget == starget) && (sas_device->id == starget->id) && (sas_device->channel == starget->channel)) sas_device->starget = NULL; + if (sas_device) { + /* + * Corresponding get() is in _scsih_target_alloc() + */ + sas_target_priv_data->sdev = NULL; + sas_device_put(sas_device); + + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); out: @@ -1386,7 +1508,7 @@ _scsih_slave_alloc(struct scsi_device *sdev) if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt2sas_get_sdev_by_addr(ioc, sas_target_priv_data->sas_address); if (sas_device && (sas_device->starget == NULL)) { sdev_printk(KERN_INFO, sdev, @@ -1394,6 +1516,10 @@ _scsih_slave_alloc(struct scsi_device *sdev) __func__, __LINE__); sas_device->starget = starget; } + + if (sas_device) + sas_device_put(sas_device); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } @@ -1428,10 +1554,13 @@ _scsih_slave_destroy(struct scsi_device *sdev) if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_target_priv_data->sas_address); + sas_device = __mpt2sas_get_sdev_from_target(ioc, + sas_target_priv_data); if (sas_device && !sas_target_priv_data->num_luns) sas_device->starget = NULL; + + if (sas_device) + sas_device_put(sas_device); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } @@ -2078,7 +2207,7 @@ _scsih_slave_configure(struct scsi_device *sdev) } spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt2sas_get_sdev_by_addr(ioc, sas_device_priv_data->sas_target->sas_address); if (!sas_device) { spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -2112,17 +2241,18 @@ _scsih_slave_configure(struct scsi_device *sdev) (unsigned long long) sas_device->enclosure_logical_id, sas_device->slot); + sas_device_put(sas_device); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (!ssp_target) _scsih_display_sata_capabilities(ioc, handle, sdev); - _scsih_change_queue_depth(sdev, qdepth); if (ssp_target) { sas_read_port_mode_page(sdev); _scsih_enable_tlr(ioc, sdev); } + return 0; } @@ -2509,8 +2639,7 @@ _scsih_tm_display_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) device_str, (unsigned long long)priv_target->sas_address); } else { spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - priv_target->sas_address); + sas_device = __mpt2sas_get_sdev_from_target(ioc, priv_target); if (sas_device) { if (priv_target->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) { @@ -2529,6 +2658,8 @@ _scsih_tm_display_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) "enclosure_logical_id(0x%016llx), slot(%d)\n", (unsigned long long)sas_device->enclosure_logical_id, sas_device->slot); + + sas_device_put(sas_device); } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } @@ -2604,12 +2735,12 @@ _scsih_dev_reset(struct scsi_cmnd *scmd) { struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host); struct MPT2SAS_DEVICE *sas_device_priv_data; - struct _sas_device *sas_device; - unsigned long flags; + struct _sas_device *sas_device = NULL; u16 handle; int r; struct scsi_target *starget = scmd->device->sdev_target; + struct MPT2SAS_TARGET *target_priv_data = starget->hostdata; starget_printk(KERN_INFO, starget, "attempting device reset! " "scmd(%p)\n", scmd); @@ -2629,12 +2760,10 @@ _scsih_dev_reset(struct scsi_cmnd *scmd) handle = 0; if (sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, - sas_device_priv_data->sas_target->handle); + sas_device = mpt2sas_get_sdev_from_target(ioc, + target_priv_data); if (sas_device) handle = sas_device->volume_handle; - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } else handle = sas_device_priv_data->sas_target->handle; @@ -2651,6 +2780,10 @@ _scsih_dev_reset(struct scsi_cmnd *scmd) out: sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n", ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + + if (sas_device) + sas_device_put(sas_device); + return r; } @@ -2665,11 +2798,11 @@ _scsih_target_reset(struct scsi_cmnd *scmd) { struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host); struct MPT2SAS_DEVICE *sas_device_priv_data; - struct _sas_device *sas_device; - unsigned long flags; + struct _sas_device *sas_device = NULL; u16 handle; int r; struct scsi_target *starget = scmd->device->sdev_target; + struct MPT2SAS_TARGET *target_priv_data = starget->hostdata; starget_printk(KERN_INFO, starget, "attempting target reset! " "scmd(%p)\n", scmd); @@ -2689,12 +2822,10 @@ _scsih_target_reset(struct scsi_cmnd *scmd) handle = 0; if (sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, - sas_device_priv_data->sas_target->handle); + sas_device = mpt2sas_get_sdev_from_target(ioc, + target_priv_data); if (sas_device) handle = sas_device->volume_handle; - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } else handle = sas_device_priv_data->sas_target->handle; @@ -2711,6 +2842,10 @@ _scsih_target_reset(struct scsi_cmnd *scmd) out: starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n", ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + + if (sas_device) + sas_device_put(sas_device); + return r; } @@ -2768,36 +2903,39 @@ _scsih_fw_event_add(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) return; spin_lock_irqsave(&ioc->fw_event_lock, flags); + fw_event_work_get(fw_event); list_add_tail(&fw_event->list, &ioc->fw_event_list); INIT_DELAYED_WORK(&fw_event->delayed_work, _firmware_event_work); + fw_event_work_get(fw_event); queue_delayed_work(ioc->firmware_event_thread, &fw_event->delayed_work, 0); spin_unlock_irqrestore(&ioc->fw_event_lock, flags); } /** - * _scsih_fw_event_free - delete fw_event + * _scsih_fw_event_del_from_list - delete fw_event from the list * @ioc: per adapter object * @fw_event: object describing the event * Context: This function will acquire ioc->fw_event_lock. * - * This removes firmware event object from link list, frees associated memory. + * If the fw_event is on the fw_event_list, remove it and do a put. * * Return nothing. */ static void -_scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work +_scsih_fw_event_del_from_list(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { unsigned long flags; spin_lock_irqsave(&ioc->fw_event_lock, flags); - list_del(&fw_event->list); - kfree(fw_event); + if (!list_empty(&fw_event->list)) { + list_del_init(&fw_event->list); + fw_event_work_put(fw_event); + } spin_unlock_irqrestore(&ioc->fw_event_lock, flags); } - /** * _scsih_error_recovery_delete_devices - remove devices not responding * @ioc: per adapter object @@ -2812,13 +2950,14 @@ _scsih_error_recovery_delete_devices(struct MPT2SAS_ADAPTER *ioc) if (ioc->is_driver_loading) return; - fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + fw_event = alloc_fw_event_work(0); if (!fw_event) return; fw_event->event = MPT2SAS_REMOVE_UNRESPONDING_DEVICES; fw_event->ioc = ioc; _scsih_fw_event_add(ioc, fw_event); + fw_event_work_put(fw_event); } /** @@ -2832,12 +2971,29 @@ mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc) { struct fw_event_work *fw_event; - fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + fw_event = alloc_fw_event_work(0); if (!fw_event) return; fw_event->event = MPT2SAS_PORT_ENABLE_COMPLETE; fw_event->ioc = ioc; _scsih_fw_event_add(ioc, fw_event); + fw_event_work_put(fw_event); +} + +static struct fw_event_work *dequeue_next_fw_event(struct MPT2SAS_ADAPTER *ioc) +{ + unsigned long flags; + struct fw_event_work *fw_event = NULL; + + spin_lock_irqsave(&ioc->fw_event_lock, flags); + if (!list_empty(&ioc->fw_event_list)) { + fw_event = list_first_entry(&ioc->fw_event_list, + struct fw_event_work, list); + list_del_init(&fw_event->list); + } + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + + return fw_event; } /** @@ -2852,17 +3008,25 @@ mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc) static void _scsih_fw_event_cleanup_queue(struct MPT2SAS_ADAPTER *ioc) { - struct fw_event_work *fw_event, *next; + struct fw_event_work *fw_event; if (list_empty(&ioc->fw_event_list) || !ioc->firmware_event_thread || in_interrupt()) return; - list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) { - if (cancel_delayed_work_sync(&fw_event->delayed_work)) { - _scsih_fw_event_free(ioc, fw_event); - continue; - } + while ((fw_event = dequeue_next_fw_event(ioc))) { + /* + * Wait on the fw_event to complete. If this returns 1, then + * the event was never executed, and we need a put for the + * reference the delayed_work had on the fw_event. + * + * If it did execute, we wait for it to finish, and the put will + * happen from _firmware_event_work() + */ + if (cancel_delayed_work_sync(&fw_event->delayed_work)) + fw_event_work_put(fw_event); + + fw_event_work_put(fw_event); } } @@ -3002,15 +3166,15 @@ _scsih_block_io_to_children_attached_to_ex(struct MPT2SAS_ADAPTER *ioc, list_for_each_entry(mpt2sas_port, &sas_expander->sas_port_list, port_list) { - if (mpt2sas_port->remote_identify.device_type == - SAS_END_DEVICE) { + if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE) { spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = - mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - mpt2sas_port->remote_identify.sas_address); - if (sas_device) + sas_device = __mpt2sas_get_sdev_by_addr(ioc, + mpt2sas_port->remote_identify.sas_address); + if (sas_device) { set_bit(sas_device->handle, - ioc->blocking_handles); + ioc->blocking_handles); + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } } @@ -3080,7 +3244,7 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) { Mpi2SCSITaskManagementRequest_t *mpi_request; u16 smid; - struct _sas_device *sas_device; + struct _sas_device *sas_device = NULL; struct MPT2SAS_TARGET *sas_target_priv_data = NULL; u64 sas_address = 0; unsigned long flags; @@ -3110,7 +3274,7 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) return; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); if (sas_device && sas_device->starget && sas_device->starget->hostdata) { sas_target_priv_data = sas_device->starget->hostdata; @@ -3131,14 +3295,14 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) if (!smid) { delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); if (!delayed_tr) - return; + goto out; INIT_LIST_HEAD(&delayed_tr->list); delayed_tr->handle = handle; list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "DELAYED:tr:handle(0x%04x), (open)\n", ioc->name, handle)); - return; + goto out; } dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "tr_send:handle(0x%04x), " @@ -3150,6 +3314,9 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) mpi_request->DevHandle = cpu_to_le16(handle); mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; mpt2sas_base_put_smid_hi_priority(ioc, smid); +out: + if (sas_device) + sas_device_put(sas_device); } @@ -4068,7 +4235,6 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, char *desc_scsi_state = ioc->tmp_string; u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo); struct _sas_device *sas_device = NULL; - unsigned long flags; struct scsi_target *starget = scmd->device->sdev_target; struct MPT2SAS_TARGET *priv_target = starget->hostdata; char *device_str = NULL; @@ -4200,9 +4366,7 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, printk(MPT2SAS_WARN_FMT "\t%s wwid(0x%016llx)\n", ioc->name, device_str, (unsigned long long)priv_target->sas_address); } else { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - priv_target->sas_address); + sas_device = mpt2sas_get_sdev_from_target(ioc, priv_target); if (sas_device) { printk(MPT2SAS_WARN_FMT "\tsas_address(0x%016llx), " "phy(%d)\n", ioc->name, sas_device->sas_address, @@ -4211,8 +4375,9 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, "\tenclosure_logical_id(0x%016llx), slot(%d)\n", ioc->name, sas_device->enclosure_logical_id, sas_device->slot); + + sas_device_put(sas_device); } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } printk(MPT2SAS_WARN_FMT "\thandle(0x%04x), ioc_status(%s)(0x%04x), " @@ -4259,7 +4424,7 @@ _scsih_turn_on_pfa_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) Mpi2SepRequest_t mpi_request; struct _sas_device *sas_device; - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); if (!sas_device) return; @@ -4274,7 +4439,7 @@ _scsih_turn_on_pfa_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) &mpi_request)) != 0) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); - return; + goto out; } sas_device->pfa_led_on = 1; @@ -4284,8 +4449,10 @@ _scsih_turn_on_pfa_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n", ioc->name, le16_to_cpu(mpi_reply.IOCStatus), le32_to_cpu(mpi_reply.IOCLogInfo))); - return; + goto out; } +out: + sas_device_put(sas_device); } /** @@ -4340,13 +4507,14 @@ _scsih_send_event_to_turn_on_pfa_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) { struct fw_event_work *fw_event; - fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + fw_event = alloc_fw_event_work(0); if (!fw_event) return; fw_event->event = MPT2SAS_TURN_ON_PFA_LED; fw_event->device_handle = handle; fw_event->ioc = ioc; _scsih_fw_event_add(ioc, fw_event); + fw_event_work_put(fw_event); } /** @@ -4370,19 +4538,17 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) /* only handle non-raid devices */ spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); if (!sas_device) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; + goto out_unlock; } starget = sas_device->starget; sas_target_priv_data = starget->hostdata; if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) || - ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } + ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) + goto out_unlock; + starget_printk(KERN_WARNING, starget, "predicted fault\n"); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -4396,7 +4562,7 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) if (!event_reply) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); - return; + goto out; } event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; @@ -4413,6 +4579,14 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address); mpt2sas_ctl_add_to_event_log(ioc, event_reply); kfree(event_reply); +out: + if (sas_device) + sas_device_put(sas_device); + return; + +out_unlock: + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + goto out; } /** @@ -5148,14 +5322,13 @@ _scsih_check_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_address = le64_to_cpu(sas_device_pg0.SASAddress); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt2sas_get_sdev_by_addr(ioc, sas_address); if (!sas_device) { printk(MPT2SAS_ERR_FMT "device is not present " "handle(0x%04x), no sas_device!!!\n", ioc->name, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; + goto out_unlock; } if (unlikely(sas_device->handle != handle)) { @@ -5172,19 +5345,24 @@ _scsih_check_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { printk(MPT2SAS_ERR_FMT "device is not present " "handle(0x%04x), flags!!!\n", ioc->name, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; + goto out_unlock; } /* check if there were any issues with discovery */ if (_scsih_check_access_status(ioc, sas_address, handle, - sas_device_pg0.AccessStatus)) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } + sas_device_pg0.AccessStatus)) + goto out_unlock; + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); _scsih_ublock_io_device(ioc, sas_address); + if (sas_device) + sas_device_put(sas_device); + return; +out_unlock: + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (sas_device) + sas_device_put(sas_device); } /** @@ -5208,7 +5386,6 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) u32 ioc_status; __le64 sas_address; u32 device_info; - unsigned long flags; if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { @@ -5250,14 +5427,13 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) return -1; } - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = mpt2sas_get_sdev_by_addr(ioc, sas_address); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) + if (sas_device) { + sas_device_put(sas_device); return 0; + } sas_device = kzalloc(sizeof(struct _sas_device), GFP_KERNEL); @@ -5267,6 +5443,7 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) return -1; } + kref_init(&sas_device->refcount); sas_device->handle = handle; if (_scsih_get_sas_address(ioc, le16_to_cpu (sas_device_pg0.ParentDevHandle), @@ -5296,6 +5473,7 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) else _scsih_sas_device_add(ioc, sas_device); + sas_device_put(sas_device); return 0; } @@ -5344,7 +5522,6 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, sas_device->handle, (unsigned long long) sas_device->sas_address)); - kfree(sas_device); } /** * _scsih_device_remove_by_handle - removing device object by handle @@ -5363,12 +5540,17 @@ _scsih_device_remove_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) return; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (sas_device) - list_del(&sas_device->list); + sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); + if (sas_device) { + list_del_init(&sas_device->list); + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) + + if (sas_device) { _scsih_remove_device(ioc, sas_device); + sas_device_put(sas_device); + } } /** @@ -5389,13 +5571,17 @@ mpt2sas_device_remove_by_sas_address(struct MPT2SAS_ADAPTER *ioc, return; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_address); - if (sas_device) - list_del(&sas_device->list); + sas_device = __mpt2sas_get_sdev_by_addr(ioc, sas_address); + if (sas_device) { + list_del_init(&sas_device->list); + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) + + if (sas_device) { _scsih_remove_device(ioc, sas_device); + sas_device_put(sas_device); + } } #ifdef CONFIG_SCSI_MPT2SAS_LOGGING /** @@ -5716,26 +5902,28 @@ _scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_address = le64_to_cpu(event_data->SASAddress); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt2sas_get_sdev_by_addr(ioc, sas_address); - if (!sas_device || !sas_device->starget) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } + if (!sas_device || !sas_device->starget) + goto out; target_priv_data = sas_device->starget->hostdata; - if (!target_priv_data) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } + if (!target_priv_data) + goto out; if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET) target_priv_data->tm_busy = 1; else target_priv_data->tm_busy = 0; + +out: + if (sas_device) + sas_device_put(sas_device); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + } #ifdef CONFIG_SCSI_MPT2SAS_LOGGING @@ -6123,7 +6311,7 @@ _scsih_sas_pd_expose(struct MPT2SAS_ADAPTER *ioc, u16 handle = le16_to_cpu(element->PhysDiskDevHandle); spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); if (sas_device) { sas_device->volume_handle = 0; sas_device->volume_wwid = 0; @@ -6142,6 +6330,8 @@ _scsih_sas_pd_expose(struct MPT2SAS_ADAPTER *ioc, /* exposing raid component */ if (starget) starget_for_each_device(starget, NULL, _scsih_reprobe_lun); + + sas_device_put(sas_device); } /** @@ -6170,7 +6360,7 @@ _scsih_sas_pd_hide(struct MPT2SAS_ADAPTER *ioc, &volume_wwid); spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle); if (sas_device) { set_bit(handle, ioc->pd_handles); if (sas_device->starget && sas_device->starget->hostdata) { @@ -6189,6 +6379,8 @@ _scsih_sas_pd_hide(struct MPT2SAS_ADAPTER *ioc, /* hiding raid component */ if (starget) starget_for_each_device(starget, (void *)1, _scsih_reprobe_lun); + + sas_device_put(sas_device); } /** @@ -6221,7 +6413,6 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc, Mpi2EventIrConfigElement_t *element) { struct _sas_device *sas_device; - unsigned long flags; u16 handle = le16_to_cpu(element->PhysDiskDevHandle); Mpi2ConfigReply_t mpi_reply; Mpi2SasDevicePage0_t sas_device_pg0; @@ -6231,11 +6422,11 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc, set_bit(handle, ioc->pd_handles); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) + sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); + if (sas_device) { + sas_device_put(sas_device); return; + } if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { @@ -6509,7 +6700,6 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u16 handle, parent_handle; u32 state; struct _sas_device *sas_device; - unsigned long flags; Mpi2ConfigReply_t mpi_reply; Mpi2SasDevicePage0_t sas_device_pg0; u32 ioc_status; @@ -6542,12 +6732,11 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, if (!ioc->is_warpdrive) set_bit(handle, ioc->pd_handles); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (sas_device) + sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); + if (sas_device) { + sas_device_put(sas_device); return; + } if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, @@ -7015,6 +7204,7 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc) struct _raid_device *raid_device, *raid_device_next; struct list_head tmp_list; unsigned long flags; + LIST_HEAD(head); printk(MPT2SAS_INFO_FMT "removing unresponding devices: start\n", ioc->name); @@ -7022,14 +7212,29 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc) /* removing unresponding end devices */ printk(MPT2SAS_INFO_FMT "removing unresponding devices: end-devices\n", ioc->name); + + /* + * Iterate, pulling off devices marked as non-responding. We become the + * owner for the reference the list had on any object we prune. + */ + spin_lock_irqsave(&ioc->sas_device_lock, flags); list_for_each_entry_safe(sas_device, sas_device_next, - &ioc->sas_device_list, list) { + &ioc->sas_device_list, list) { if (!sas_device->responding) - mpt2sas_device_remove_by_sas_address(ioc, - sas_device->sas_address); + list_move_tail(&sas_device->list, &head); else sas_device->responding = 0; } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + /* + * Now, uninitialize and remove the unresponding devices we pruned. + */ + list_for_each_entry_safe(sas_device, sas_device_next, &head, list) { + _scsih_remove_device(ioc, sas_device); + list_del_init(&sas_device->list); + sas_device_put(sas_device); + } /* removing unresponding volumes */ if (ioc->ir_firmware) { @@ -7179,11 +7384,11 @@ _scsih_scan_for_devices_after_reset(struct MPT2SAS_ADAPTER *ioc) } phys_disk_num = pd_pg0.PhysDiskNum; handle = le16_to_cpu(pd_pg0.DevHandle); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) + sas_device = mpt2sas_get_sdev_by_handle(ioc, handle); + if (sas_device) { + sas_device_put(sas_device); continue; + } if (mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle) != 0) @@ -7302,12 +7507,12 @@ _scsih_scan_for_devices_after_reset(struct MPT2SAS_ADAPTER *ioc) if (!(_scsih_is_end_device( le32_to_cpu(sas_device_pg0.DeviceInfo)))) continue; - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = mpt2sas_get_sdev_by_addr(ioc, le64_to_cpu(sas_device_pg0.SASAddress)); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) + if (sas_device) { + sas_device_put(sas_device); continue; + } parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) { printk(MPT2SAS_INFO_FMT "\tBEFORE adding end device: " @@ -7410,17 +7615,27 @@ _firmware_event_work(struct work_struct *work) struct fw_event_work, delayed_work.work); struct MPT2SAS_ADAPTER *ioc = fw_event->ioc; + _scsih_fw_event_del_from_list(ioc, fw_event); + /* the queue is being flushed so ignore this event */ - if (ioc->remove_host || - ioc->pci_error_recovery) { - _scsih_fw_event_free(ioc, fw_event); + if (ioc->remove_host || ioc->pci_error_recovery) { + fw_event_work_put(fw_event); return; } switch (fw_event->event) { case MPT2SAS_REMOVE_UNRESPONDING_DEVICES: - while (scsi_host_in_recovery(ioc->shost) || ioc->shost_recovery) + while (scsi_host_in_recovery(ioc->shost) || + ioc->shost_recovery) { + /* + * If we're unloading, bail. Otherwise, this can become + * an infinite loop. + */ + if (ioc->remove_host) + goto out; + ssleep(1); + } _scsih_remove_unresponding_sas_devices(ioc); _scsih_scan_for_devices_after_reset(ioc); break; @@ -7469,7 +7684,8 @@ _firmware_event_work(struct work_struct *work) _scsih_sas_ir_operation_status_event(ioc, fw_event); break; } - _scsih_fw_event_free(ioc, fw_event); +out: + fw_event_work_put(fw_event); } /** @@ -7607,7 +7823,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, } sz = le16_to_cpu(mpi_reply->EventDataLength) * 4; - fw_event = kzalloc(sizeof(*fw_event) + sz, GFP_ATOMIC); + fw_event = alloc_fw_event_work(sz); if (!fw_event) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); @@ -7620,6 +7836,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, fw_event->VP_ID = mpi_reply->VP_ID; fw_event->event = event; _scsih_fw_event_add(ioc, fw_event); + fw_event_work_put(fw_event); return; } @@ -7867,7 +8084,9 @@ _scsih_remove(struct pci_dev *pdev) sas_remove_host(shost); scsi_remove_host(shost); mpt2sas_base_detach(ioc); + spin_lock(&gioc_lock); list_del(&ioc->list); + spin_unlock(&gioc_lock); scsi_host_put(shost); } @@ -7966,6 +8185,48 @@ _scsih_probe_raid(struct MPT2SAS_ADAPTER *ioc) } } +static struct _sas_device *get_next_sas_device(struct MPT2SAS_ADAPTER *ioc) +{ + struct _sas_device *sas_device = NULL; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + if (!list_empty(&ioc->sas_device_init_list)) { + sas_device = list_first_entry(&ioc->sas_device_init_list, + struct _sas_device, list); + sas_device_get(sas_device); + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + return sas_device; +} + +static void sas_device_make_active(struct MPT2SAS_ADAPTER *ioc, + struct _sas_device *sas_device) +{ + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + + /* + * Since we dropped the lock during the call to port_add(), we need to + * be careful here that somebody else didn't move or delete this item + * while we were busy with other things. + * + * If it was on the list, we need a put() for the reference the list + * had. Either way, we need a get() for the destination list. + */ + if (!list_empty(&sas_device->list)) { + list_del_init(&sas_device->list); + sas_device_put(sas_device); + } + + sas_device_get(sas_device); + list_add_tail(&sas_device->list, &ioc->sas_device_list); + + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); +} + /** * _scsih_probe_sas - reporting sas devices to sas transport * @ioc: per adapter object @@ -7975,34 +8236,30 @@ _scsih_probe_raid(struct MPT2SAS_ADAPTER *ioc) static void _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc) { - struct _sas_device *sas_device, *next; - unsigned long flags; - - /* SAS Device List */ - list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list, - list) { + struct _sas_device *sas_device; - if (ioc->hide_drives) - continue; + if (ioc->hide_drives) + return; + while ((sas_device = get_next_sas_device(ioc))) { if (!mpt2sas_transport_port_add(ioc, sas_device->handle, - sas_device->sas_address_parent)) { - list_del(&sas_device->list); - kfree(sas_device); + sas_device->sas_address_parent)) { + _scsih_sas_device_remove(ioc, sas_device); + sas_device_put(sas_device); continue; } else if (!sas_device->starget) { if (!ioc->is_driver_loading) { mpt2sas_transport_port_remove(ioc, - sas_device->sas_address, - sas_device->sas_address_parent); - list_del(&sas_device->list); - kfree(sas_device); + sas_device->sas_address, + sas_device->sas_address_parent); + _scsih_sas_device_remove(ioc, sas_device); + sas_device_put(sas_device); continue; } } - spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_move_tail(&sas_device->list, &ioc->sas_device_list); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + sas_device_make_active(ioc, sas_device); + sas_device_put(sas_device); } } @@ -8142,7 +8399,9 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc = shost_priv(shost); memset(ioc, 0, sizeof(struct MPT2SAS_ADAPTER)); INIT_LIST_HEAD(&ioc->list); + spin_lock(&gioc_lock); list_add_tail(&ioc->list, &mpt2sas_ioc_list); + spin_unlock(&gioc_lock); ioc->shost = shost; ioc->id = mpt_ids++; sprintf(ioc->name, "%s%d", MPT2SAS_DRIVER_NAME, ioc->id); @@ -8167,6 +8426,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds; /* misc semaphores and spin locks */ mutex_init(&ioc->reset_in_progress_mutex); + /* initializing pci_access_mutex lock */ + mutex_init(&ioc->pci_access_mutex); spin_lock_init(&ioc->ioc_reset_in_progress_lock); spin_lock_init(&ioc->scsi_lookup_lock); spin_lock_init(&ioc->sas_device_lock); @@ -8269,7 +8530,9 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) out_attach_fail: destroy_workqueue(ioc->firmware_event_thread); out_thread_fail: + spin_lock(&gioc_lock); list_del(&ioc->list); + spin_unlock(&gioc_lock); scsi_host_put(shost); return rv; } diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c index ff2500ab9ba4..af868009395d 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_transport.c +++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c @@ -1323,15 +1323,17 @@ _transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) int rc; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt2sas_get_sdev_by_addr(ioc, rphy->identify.sas_address); if (sas_device) { *identifier = sas_device->enclosure_logical_id; rc = 0; + sas_device_put(sas_device); } else { *identifier = 0; rc = -ENXIO; } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); return rc; } @@ -1351,12 +1353,14 @@ _transport_get_bay_identifier(struct sas_rphy *rphy) int rc; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt2sas_get_sdev_by_addr(ioc, rphy->identify.sas_address); - if (sas_device) + if (sas_device) { rc = sas_device->slot; - else + sas_device_put(sas_device); + } else { rc = -ENXIO; + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); return rc; } diff --git a/drivers/scsi/mpt3sas/mpi/mpi2.h b/drivers/scsi/mpt3sas/mpi/mpi2.h index c34c1157907b..ec27ad2d186f 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2.h @@ -8,7 +8,7 @@ * scatter/gather formats. * Creation Date: June 21, 2006 * - * mpi2.h Version: 02.00.31 + * mpi2.h Version: 02.00.35 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -88,6 +88,10 @@ * Added MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET. * 04-09-13 02.00.30 Bumped MPI2_HEADER_VERSION_UNIT. * 04-17-13 02.00.31 Bumped MPI2_HEADER_VERSION_UNIT. + * 08-19-13 02.00.32 Bumped MPI2_HEADER_VERSION_UNIT. + * 12-05-13 02.00.33 Bumped MPI2_HEADER_VERSION_UNIT. + * 01-08-14 02.00.34 Bumped MPI2_HEADER_VERSION_UNIT + * 06-13-14 02.00.35 Bumped MPI2_HEADER_VERSION_UNIT. * -------------------------------------------------------------------------- */ @@ -121,7 +125,7 @@ #define MPI2_VERSION_02_05 (0x0205) /*Unit and Dev versioning for this MPI header set */ -#define MPI2_HEADER_VERSION_UNIT (0x1F) +#define MPI2_HEADER_VERSION_UNIT (0x23) #define MPI2_HEADER_VERSION_DEV (0x00) #define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) #define MPI2_HEADER_VERSION_UNIT_SHIFT (8) diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h index e261a3153bb3..581fdb375db5 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h @@ -6,7 +6,7 @@ * Title: MPI Configuration messages and pages * Creation Date: November 10, 2006 * - * mpi2_cnfg.h Version: 02.00.26 + * mpi2_cnfg.h Version: 02.00.29 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -165,6 +165,20 @@ * match the specification. * 08-19-13 02.00.26 Added reserved words to MPI2_CONFIG_PAGE_IO_UNIT_7 for * future use. + * 12-05-13 02.00.27 Added MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL for + * MPI2_CONFIG_PAGE_MAN_7. + * Added EnclosureLevel and ConnectorName fields to + * MPI2_CONFIG_PAGE_SAS_DEV_0. + * Added MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID for + * MPI2_CONFIG_PAGE_SAS_DEV_0. + * Added EnclosureLevel field to + * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. + * Added MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID for + * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. + * 01-08-14 02.00.28 Added more defines for the BiosOptions field of + * MPI2_CONFIG_PAGE_BIOS_1. + * 06-13-14 02.00.29 Added SSUTimeout field to MPI2_CONFIG_PAGE_BIOS_1, and + * more defines for the BiosOptions field.. * -------------------------------------------------------------------------- */ @@ -724,6 +738,7 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_7 { #define MPI2_MANUFACTURING7_PAGEVERSION (0x01) /*defines for the Flags field */ +#define MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL (0x00000008) #define MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER (0x00000002) #define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001) @@ -1311,7 +1326,9 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_1 { MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ U32 BiosOptions; /*0x04 */ U32 IOCSettings; /*0x08 */ - U32 Reserved1; /*0x0C */ + U8 SSUTimeout; /*0x0C */ + U8 Reserved1; /*0x0D */ + U16 Reserved2; /*0x0E */ U32 DeviceSettings; /*0x10 */ U16 NumberOfDevices; /*0x14 */ U16 UEFIVersion; /*0x16 */ @@ -1323,9 +1340,24 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_1 { *PTR_MPI2_CONFIG_PAGE_BIOS_1, Mpi2BiosPage1_t, *pMpi2BiosPage1_t; -#define MPI2_BIOSPAGE1_PAGEVERSION (0x05) +#define MPI2_BIOSPAGE1_PAGEVERSION (0x07) /*values for BIOS Page 1 BiosOptions field */ +#define MPI2_BIOSPAGE1_OPTIONS_PNS_MASK (0x00003800) +#define MPI2_BIOSPAGE1_OPTIONS_PNS_PBDHL (0x00000000) +#define MPI2_BIOSPAGE1_OPTIONS_PNS_ENCSLOSURE (0x00000800) +#define MPI2_BIOSPAGE1_OPTIONS_PNS_LWWID (0x00001000) +#define MPI2_BIOSPAGE1_OPTIONS_PNS_PSENS (0x00001800) +#define MPI2_BIOSPAGE1_OPTIONS_PNS_ESPHY (0x00002000) + +#define MPI2_BIOSPAGE1_OPTIONS_X86_DISABLE_BIOS (0x00000400) + +#define MPI2_BIOSPAGE1_OPTIONS_MASK_REGISTRATION_UEFI_BSD (0x00000300) +#define MPI2_BIOSPAGE1_OPTIONS_USE_BIT0_REGISTRATION_UEFI_BSD (0x00000000) +#define MPI2_BIOSPAGE1_OPTIONS_FULL_REGISTRATION_UEFI_BSD (0x00000100) +#define MPI2_BIOSPAGE1_OPTIONS_ADAPTER_REGISTRATION_UEFI_BSD (0x00000200) +#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_REGISTRATION_UEFI_BSD (0x00000300) + #define MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID (0x000000F0) #define MPI2_BIOSPAGE1_OPTIONS_LSI_OEM_ID (0x00000000) @@ -2633,9 +2665,9 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 { U8 ControlGroup; /*0x2E */ U8 - Reserved1; /*0x2F */ + EnclosureLevel; /*0x2F */ U32 - Reserved2; /*0x30 */ + ConnectorName[4]; /*0x30 */ U32 Reserved3; /*0x34 */ } MPI2_CONFIG_PAGE_SAS_DEV_0, @@ -2643,7 +2675,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 { Mpi2SasDevicePage0_t, *pMpi2SasDevicePage0_t; -#define MPI2_SASDEVICE0_PAGEVERSION (0x08) +#define MPI2_SASDEVICE0_PAGEVERSION (0x09) /*values for SAS Device Page 0 AccessStatus field */ #define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS (0x00) @@ -2683,6 +2715,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 { #define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED (0x0020) #define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED (0x0010) #define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH (0x0008) +#define MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID (0x0002) #define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001) @@ -3019,8 +3052,10 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 { NumSlots; /*0x18 */ U16 StartSlot; /*0x1A */ - U16 + U8 Reserved2; /*0x1C */ + U8 + EnclosureLevel; /*0x1D */ U16 SEPDevHandle; /*0x1E */ U32 @@ -3031,9 +3066,10 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 { *PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, Mpi2SasEnclosurePage0_t, *pMpi2SasEnclosurePage0_t; -#define MPI2_SASENCLOSURE0_PAGEVERSION (0x03) +#define MPI2_SASENCLOSURE0_PAGEVERSION (0x04) /*values for SAS Enclosure Page 0 Flags field */ +#define MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID (0x0010) #define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK (0x000F) #define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000) #define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES (0x0001) diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h index 490830957806..d7598cc4bb8e 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h @@ -6,7 +6,7 @@ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages * Creation Date: October 11, 2006 * - * mpi2_ioc.h Version: 02.00.23 + * mpi2_ioc.h Version: 02.00.24 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -132,6 +132,7 @@ * Added MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE. * Added MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY. * Added Encrypted Hash Extended Image. + * 12-05-13 02.00.24 Added MPI25_HASH_IMAGE_TYPE_BIOS. * -------------------------------------------------------------------------- */ @@ -1598,6 +1599,7 @@ Mpi25EncryptedHashEntry_t, *pMpi25EncryptedHashEntry_t; /* values for HashImageType */ #define MPI25_HASH_IMAGE_TYPE_UNUSED (0x00) #define MPI25_HASH_IMAGE_TYPE_FIRMWARE (0x01) +#define MPI25_HASH_IMAGE_TYPE_BIOS (0x02) /* values for HashAlgorithm */ #define MPI25_HASH_ALGORITHM_UNUSED (0x00) diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_tool.h b/drivers/scsi/mpt3sas/mpi/mpi2_tool.h index 904910d8a737..1629e5bce7e1 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_tool.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_tool.h @@ -6,7 +6,7 @@ * Title: MPI diagnostic tool structures and definitions * Creation Date: March 26, 2007 * - * mpi2_tool.h Version: 02.00.11 + * mpi2_tool.h Version: 02.00.12 * * Version History * --------------- @@ -33,6 +33,7 @@ * 07-26-12 02.00.10 Modified MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST so that * it uses MPI Chain SGE as well as MPI Simple SGE. * 08-19-13 02.00.11 Added MPI2_TOOLBOX_TEXT_DISPLAY_TOOL and related info. + * 01-08-14 02.00.12 Added MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC. * -------------------------------------------------------------------------- */ @@ -100,6 +101,7 @@ typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST { #define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000) #define MPI2_TOOLBOX_CLEAN_FW_CURRENT (0x10000000) #define MPI2_TOOLBOX_CLEAN_FW_BACKUP (0x08000000) +#define MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC (0x04000000) #define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000) #define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000) #define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 43f87e904b98..d4f1dcdb8361 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -83,10 +83,10 @@ static int msix_disable = -1; module_param(msix_disable, int, 0); MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)"); -static int max_msix_vectors = 8; +static int max_msix_vectors = -1; module_param(max_msix_vectors, int, 0); MODULE_PARM_DESC(max_msix_vectors, - " max msix vectors - (default=8)"); + " max msix vectors"); static int mpt3sas_fwfault_debug; MODULE_PARM_DESC(mpt3sas_fwfault_debug, @@ -1009,8 +1009,30 @@ _base_interrupt(int irq, void *bus_id) } wmb(); - writel(reply_q->reply_post_host_index | (msix_index << - MPI2_RPHI_MSIX_INDEX_SHIFT), &ioc->chip->ReplyPostHostIndex); + + /* Update Reply Post Host Index. + * For those HBA's which support combined reply queue feature + * 1. Get the correct Supplemental Reply Post Host Index Register. + * i.e. (msix_index / 8)th entry from Supplemental Reply Post Host + * Index Register address bank i.e replyPostRegisterIndex[], + * 2. Then update this register with new reply host index value + * in ReplyPostIndex field and the MSIxIndex field with + * msix_index value reduced to a value between 0 and 7, + * using a modulo 8 operation. Since each Supplemental Reply Post + * Host Index Register supports 8 MSI-X vectors. + * + * For other HBA's just update the Reply Post Host Index register with + * new reply host index value in ReplyPostIndex Field and msix_index + * value in MSIxIndex field. + */ + if (ioc->msix96_vector) + writel(reply_q->reply_post_host_index | ((msix_index & 7) << + MPI2_RPHI_MSIX_INDEX_SHIFT), + ioc->replyPostRegisterIndex[msix_index/8]); + else + writel(reply_q->reply_post_host_index | (msix_index << + MPI2_RPHI_MSIX_INDEX_SHIFT), + &ioc->chip->ReplyPostHostIndex); atomic_dec(&reply_q->busy); return IRQ_HANDLED; } @@ -1338,7 +1360,7 @@ _base_build_sg_scmd_ieee(struct MPT3SAS_ADAPTER *ioc, sg_scmd = scsi_sglist(scmd); sges_left = scsi_dma_map(scmd); - if (!sges_left) { + if (sges_left < 0) { sdev_printk(KERN_ERR, scmd->device, "pci_map_sg failed: request for %d bytes!\n", scsi_bufflen(scmd)); @@ -1407,7 +1429,7 @@ _base_build_sg_scmd_ieee(struct MPT3SAS_ADAPTER *ioc, fill_in_last_segment: /* fill the last segment */ - while (sges_left) { + while (sges_left > 0) { if (sges_left == 1) _base_add_sg_single_ieee(sg_local, simple_sgl_flags_last, 0, sg_dma_len(sg_scmd), @@ -1560,8 +1582,6 @@ _base_check_enable_msix(struct MPT3SAS_ADAPTER *ioc) pci_read_config_word(ioc->pdev, base + 2, &message_control); ioc->msix_vector_count = (message_control & 0x3FF) + 1; - if (ioc->msix_vector_count > 8) - ioc->msix_vector_count = 8; dinitprintk(ioc, pr_info(MPT3SAS_FMT "msix is supported, vector_count(%d)\n", ioc->name, ioc->msix_vector_count)); @@ -1793,6 +1813,36 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc) } /** + * mpt3sas_base_unmap_resources - free controller resources + * @ioc: per adapter object + */ +void +mpt3sas_base_unmap_resources(struct MPT3SAS_ADAPTER *ioc) +{ + struct pci_dev *pdev = ioc->pdev; + + dexitprintk(ioc, printk(MPT3SAS_FMT "%s\n", + ioc->name, __func__)); + + _base_free_irq(ioc); + _base_disable_msix(ioc); + + if (ioc->msix96_vector) + kfree(ioc->replyPostRegisterIndex); + + if (ioc->chip_phys) { + iounmap(ioc->chip); + ioc->chip_phys = 0; + } + + if (pci_is_enabled(pdev)) { + pci_release_selected_regions(ioc->pdev, ioc->bars); + pci_disable_pcie_error_reporting(pdev); + pci_disable_device(pdev); + } +} + +/** * mpt3sas_base_map_resources - map in controller resources (io/irq/memap) * @ioc: per adapter object * @@ -1882,6 +1932,36 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) if (r) goto out_fail; + /* Use the Combined reply queue feature only for SAS3 C0 & higher + * revision HBAs and also only when reply queue count is greater than 8 + */ + if (ioc->msix96_vector && ioc->reply_queue_count > 8) { + /* Determine the Supplemental Reply Post Host Index Registers + * Addresse. Supplemental Reply Post Host Index Registers + * starts at offset MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET and + * each register is at offset bytes of + * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET from previous one. + */ + ioc->replyPostRegisterIndex = kcalloc( + MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT, + sizeof(resource_size_t *), GFP_KERNEL); + if (!ioc->replyPostRegisterIndex) { + dfailprintk(ioc, printk(MPT3SAS_FMT + "allocation for reply Post Register Index failed!!!\n", + ioc->name)); + r = -ENOMEM; + goto out_fail; + } + + for (i = 0; i < MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT; i++) { + ioc->replyPostRegisterIndex[i] = (resource_size_t *) + ((u8 *)&ioc->chip->Doorbell + + MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET + + (i * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET)); + } + } else + ioc->msix96_vector = 0; + list_for_each_entry(reply_q, &ioc->reply_queue_list, list) pr_info(MPT3SAS_FMT "%s: IRQ %d\n", reply_q->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" : @@ -1897,12 +1977,7 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) return 0; out_fail: - if (ioc->chip_phys) - iounmap(ioc->chip); - ioc->chip_phys = 0; - pci_release_selected_regions(ioc->pdev, ioc->bars); - pci_disable_pcie_error_reporting(pdev); - pci_disable_device(pdev); + mpt3sas_base_unmap_resources(ioc); return r; } @@ -2292,6 +2367,99 @@ _base_display_intel_branding(struct MPT3SAS_ADAPTER *ioc) /** + * _base_display_dell_branding - Display branding string + * @ioc: per adapter object + * + * Return nothing. + */ +static void +_base_display_dell_branding(struct MPT3SAS_ADAPTER *ioc) +{ + if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_DELL) + return; + + switch (ioc->pdev->device) { + case MPI25_MFGPAGE_DEVID_SAS3008: + switch (ioc->pdev->subsystem_device) { + case MPT3SAS_DELL_12G_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_DELL_12G_HBA_BRANDING); + break; + default: + pr_info(MPT3SAS_FMT + "Dell 12Gbps HBA: Subsystem ID: 0x%X\n", ioc->name, + ioc->pdev->subsystem_device); + break; + } + break; + default: + pr_info(MPT3SAS_FMT + "Dell 12Gbps HBA: Subsystem ID: 0x%X\n", ioc->name, + ioc->pdev->subsystem_device); + break; + } +} + +/** + * _base_display_cisco_branding - Display branding string + * @ioc: per adapter object + * + * Return nothing. + */ +static void +_base_display_cisco_branding(struct MPT3SAS_ADAPTER *ioc) +{ + if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_CISCO) + return; + + switch (ioc->pdev->device) { + case MPI25_MFGPAGE_DEVID_SAS3008: + switch (ioc->pdev->subsystem_device) { + case MPT3SAS_CISCO_12G_8E_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_CISCO_12G_8E_HBA_BRANDING); + break; + case MPT3SAS_CISCO_12G_8I_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_CISCO_12G_8I_HBA_BRANDING); + break; + case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING); + break; + default: + pr_info(MPT3SAS_FMT + "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } + break; + case MPI25_MFGPAGE_DEVID_SAS3108_1: + switch (ioc->pdev->subsystem_device) { + case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING); + break; + case MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING); + break; + default: + pr_info(MPT3SAS_FMT + "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } + break; + default: + pr_info(MPT3SAS_FMT + "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } +} + +/** * _base_display_ioc_capabilities - Disply IOC's capabilities. * @ioc: per adapter object * @@ -2321,6 +2489,8 @@ _base_display_ioc_capabilities(struct MPT3SAS_ADAPTER *ioc) bios_version & 0x000000FF); _base_display_intel_branding(ioc); + _base_display_dell_branding(ioc); + _base_display_cisco_branding(ioc); pr_info(MPT3SAS_FMT "Protocol=(", ioc->name); @@ -3139,6 +3309,9 @@ _base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout, * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell. */ static int +_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag); + +static int _base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, int sleep_flag) { @@ -3681,6 +3854,64 @@ _base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag) } /** + * _base_wait_for_iocstate - Wait until the card is in READY or OPERATIONAL + * @ioc: per adapter object + * @timeout: + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout, + int sleep_flag) +{ + u32 ioc_state; + int rc; + + dinitprintk(ioc, printk(MPT3SAS_FMT "%s\n", ioc->name, + __func__)); + + if (ioc->pci_error_recovery) { + dfailprintk(ioc, printk(MPT3SAS_FMT + "%s: host in pci error recovery\n", ioc->name, __func__)); + return -EFAULT; + } + + ioc_state = mpt3sas_base_get_iocstate(ioc, 0); + dhsprintk(ioc, printk(MPT3SAS_FMT "%s: ioc_state(0x%08x)\n", + ioc->name, __func__, ioc_state)); + + if (((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) || + (ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) + return 0; + + if (ioc_state & MPI2_DOORBELL_USED) { + dhsprintk(ioc, printk(MPT3SAS_FMT + "unexpected doorbell active!\n", ioc->name)); + goto issue_diag_reset; + } + + if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { + mpt3sas_base_fault_info(ioc, ioc_state & + MPI2_DOORBELL_DATA_MASK); + goto issue_diag_reset; + } + + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, + timeout, sleep_flag); + if (ioc_state) { + dfailprintk(ioc, printk(MPT3SAS_FMT + "%s: failed going to ready state (ioc_state=0x%x)\n", + ioc->name, __func__, ioc_state)); + return -EFAULT; + } + + issue_diag_reset: + rc = _base_diag_reset(ioc, sleep_flag); + return rc; +} + +/** * _base_get_ioc_facts - obtain ioc facts reply and save in ioc * @ioc: per adapter object * @sleep_flag: CAN_SLEEP or NO_SLEEP @@ -3698,6 +3929,13 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); + r = _base_wait_for_iocstate(ioc, 10, sleep_flag); + if (r) { + dfailprintk(ioc, printk(MPT3SAS_FMT + "%s: failed getting to correct state\n", + ioc->name, __func__)); + return r; + } mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t); mpi_request_sz = sizeof(Mpi2IOCFactsRequest_t); memset(&mpi_request, 0, mpi_request_sz); @@ -3783,7 +4021,7 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER; mpi_request.VF_ID = 0; /* TODO */ mpi_request.VP_ID = 0; - mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION); + mpi_request.MsgVersion = cpu_to_le16(MPI25_VERSION); mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION); if (_base_is_controller_msix_enabled(ioc)) @@ -4524,8 +4762,15 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) /* initialize reply post host index */ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { - writel(reply_q->msix_index << MPI2_RPHI_MSIX_INDEX_SHIFT, - &ioc->chip->ReplyPostHostIndex); + if (ioc->msix96_vector) + writel((reply_q->msix_index & 7)<< + MPI2_RPHI_MSIX_INDEX_SHIFT, + ioc->replyPostRegisterIndex[reply_q->msix_index/8]); + else + writel(reply_q->msix_index << + MPI2_RPHI_MSIX_INDEX_SHIFT, + &ioc->chip->ReplyPostHostIndex); + if (!_base_is_controller_msix_enabled(ioc)) goto skip_init_reply_post_host_index; } @@ -4564,8 +4809,6 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) void mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc) { - struct pci_dev *pdev = ioc->pdev; - dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); @@ -4576,18 +4819,7 @@ mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc) ioc->shost_recovery = 0; } - _base_free_irq(ioc); - _base_disable_msix(ioc); - - if (ioc->chip_phys && ioc->chip) - iounmap(ioc->chip); - ioc->chip_phys = 0; - - if (pci_is_enabled(pdev)) { - pci_release_selected_regions(ioc->pdev, ioc->bars); - pci_disable_pcie_error_reporting(pdev); - pci_disable_device(pdev); - } + mpt3sas_base_unmap_resources(ioc); return; } @@ -4602,6 +4834,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) { int r, i; int cpu_id, last_cpu_id = 0; + u8 revision; dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); @@ -4621,6 +4854,20 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) goto out_free_resources; } + /* Check whether the controller revision is C0 or above. + * only C0 and above revision controllers support 96 MSI-X vectors. + */ + revision = ioc->pdev->revision; + + if ((ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3004 || + ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3008 || + ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3108_1 || + ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3108_2 || + ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3108_5 || + ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3108_6) && + (revision >= 0x02)) + ioc->msix96_vector = 1; + ioc->rdpq_array_enable_assigned = 0; ioc->dma_mask = 0; r = mpt3sas_base_map_resources(ioc); @@ -4643,7 +4890,6 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) ioc->build_sg_scmd = &_base_build_sg_scmd_ieee; ioc->build_sg = &_base_build_sg_ieee; ioc->build_zero_len_sge = &_base_build_zero_len_sge_ieee; - ioc->mpi25 = 1; ioc->sge_size_ieee = sizeof(Mpi2IeeeSgeSimple64_t); /* diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index afa881682bef..f0e462b0880d 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -71,8 +71,8 @@ #define MPT3SAS_DRIVER_NAME "mpt3sas" #define MPT3SAS_AUTHOR "Avago Technologies <MPT-FusionLinux.pdl@avagotech.com>" #define MPT3SAS_DESCRIPTION "LSI MPT Fusion SAS 3.0 Device Driver" -#define MPT3SAS_DRIVER_VERSION "04.100.00.00" -#define MPT3SAS_MAJOR_VERSION 4 +#define MPT3SAS_DRIVER_VERSION "09.100.00.00" +#define MPT3SAS_MAJOR_VERSION 9 #define MPT3SAS_MINOR_VERSION 100 #define MPT3SAS_BUILD_VERSION 0 #define MPT3SAS_RELEASE_VERSION 00 @@ -152,12 +152,49 @@ #define MPT3SAS_INTEL_RS3UC080_SSDID 0x3524 /* + * Dell HBA branding + */ +#define MPT3SAS_DELL_12G_HBA_BRANDING \ + "Dell 12Gbps HBA" + +/* + * Dell HBA SSDIDs + */ +#define MPT3SAS_DELL_12G_HBA_SSDID 0x1F46 + +/* + * Cisco HBA branding + */ +#define MPT3SAS_CISCO_12G_8E_HBA_BRANDING \ + "Cisco 9300-8E 12G SAS HBA" +#define MPT3SAS_CISCO_12G_8I_HBA_BRANDING \ + "Cisco 9300-8i 12G SAS HBA" +#define MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING \ + "Cisco 12G Modular SAS Pass through Controller" +#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING \ + "UCS C3X60 12G SAS Pass through Controller" +/* + * Cisco HBA SSSDIDs + */ +#define MPT3SAS_CISCO_12G_8E_HBA_SSDID 0x14C +#define MPT3SAS_CISCO_12G_8I_HBA_SSDID 0x154 +#define MPT3SAS_CISCO_12G_AVILA_HBA_SSDID 0x155 +#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID 0x156 + +/* * status bits for ioc->diag_buffer_status */ #define MPT3_DIAG_BUFFER_IS_REGISTERED (0x01) #define MPT3_DIAG_BUFFER_IS_RELEASED (0x02) #define MPT3_DIAG_BUFFER_IS_DIAG_RESET (0x04) +/* + * Combined Reply Queue constants, + * There are twelve Supplemental Reply Post Host Index Registers + * and each register is at offset 0x10 bytes from the previous one. + */ +#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT 12 +#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET (0x10) /* OEM Identifiers */ #define MFG10_OEM_ID_INVALID (0x00000000) @@ -173,6 +210,8 @@ #define MFG10_GF0_SSD_DATA_SCRUB_DISABLE (0x00000008) #define MFG10_GF0_SINGLE_DRIVE_R0 (0x00000010) +#define VIRTUAL_IO_FAILED_RETRY (0x32010081) + /* OEM Specific Flags will come from OEM specific header files */ struct Mpi2ManufacturingPage10_t { MPI2_CONFIG_PAGE_HEADER Header; /* 00h */ @@ -294,7 +333,8 @@ struct _internal_cmd { * @responding: used in _scsih_sas_device_mark_responding * @fast_path: fast path feature enable bit * @pfa_led_on: flag for PFA LED status - * + * @pend_sas_rphy_add: flag to check if device is in sas_rphy_add() + * addition routine. */ struct _sas_device { struct list_head list; @@ -315,6 +355,9 @@ struct _sas_device { u8 responding; u8 fast_path; u8 pfa_led_on; + u8 pend_sas_rphy_add; + u8 enclosure_level; + u8 connector_name[4]; }; /** @@ -728,7 +771,8 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); * is assigned only ones * @reply_queue_count: number of reply queue's * @reply_queue_list: link list contaning the reply queue info - * @reply_post_host_index: head index in the pool where FW completes IO + * @msix96_vector: 96 MSI-X vector support + * @replyPostRegisterIndex: index of next position in Reply Desc Post Queue * @delayed_tr_list: target reset link list * @delayed_tr_volume_list: volume target reset link list * @@temp_sensors_count: flag to carry the number of temperature sensors @@ -814,7 +858,6 @@ struct MPT3SAS_ADAPTER { MPT_BUILD_SG_SCMD build_sg_scmd; MPT_BUILD_SG build_sg; MPT_BUILD_ZERO_LEN_SGE build_zero_len_sge; - u8 mpi25; u16 sge_size_ieee; /* function ptr for MPI sg elements only */ @@ -937,6 +980,10 @@ struct MPT3SAS_ADAPTER { u8 reply_queue_count; struct list_head reply_queue_list; + u8 msix96_vector; + /* reply post register index */ + resource_size_t **replyPostRegisterIndex; + struct list_head delayed_tr_list; struct list_head delayed_tr_volume_list; u8 temp_sensors_count; diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 5a97e3286719..8ccef38523fa 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -585,6 +585,22 @@ _scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc, if (!sas_device) return; + pr_info(MPT3SAS_FMT + "removing handle(0x%04x), sas_addr(0x%016llx)\n", + ioc->name, sas_device->handle, + (unsigned long long) sas_device->sas_address); + + if (sas_device->enclosure_handle != 0) + pr_info(MPT3SAS_FMT + "removing enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, (unsigned long long) + sas_device->enclosure_logical_id, sas_device->slot); + + if (sas_device->connector_name[0] != '\0') + pr_info(MPT3SAS_FMT + "removing enclosure level(0x%04x), connector name( %s)\n", + ioc->name, sas_device->enclosure_level, + sas_device->connector_name); spin_lock_irqsave(&ioc->sas_device_lock, flags); list_del(&sas_device->list); @@ -663,6 +679,18 @@ _scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc, ioc->name, __func__, sas_device->handle, (unsigned long long)sas_device->sas_address)); + if (sas_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure logical id(0x%016llx), slot( %d)\n", + ioc->name, __func__, (unsigned long long) + sas_device->enclosure_logical_id, sas_device->slot)); + + if (sas_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure level(0x%04x), connector name( %s)\n", + ioc->name, __func__, + sas_device->enclosure_level, sas_device->connector_name)); + spin_lock_irqsave(&ioc->sas_device_lock, flags); list_add_tail(&sas_device->list, &ioc->sas_device_list); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -704,6 +732,18 @@ _scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc, __func__, sas_device->handle, (unsigned long long)sas_device->sas_address)); + if (sas_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure logical id(0x%016llx), slot( %d)\n", + ioc->name, __func__, (unsigned long long) + sas_device->enclosure_logical_id, sas_device->slot)); + + if (sas_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure level(0x%04x), connector name( %s)\n", + ioc->name, __func__, sas_device->enclosure_level, + sas_device->connector_name)); + spin_lock_irqsave(&ioc->sas_device_lock, flags); list_add_tail(&sas_device->list, &ioc->sas_device_init_list); _scsih_determine_boot_device(ioc, sas_device, 0); @@ -1772,10 +1812,16 @@ _scsih_slave_configure(struct scsi_device *sdev) "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n", ds, handle, (unsigned long long)sas_device->sas_address, sas_device->phy, (unsigned long long)sas_device->device_name); - sdev_printk(KERN_INFO, sdev, - "%s: enclosure_logical_id(0x%016llx), slot(%d)\n", - ds, (unsigned long long) - sas_device->enclosure_logical_id, sas_device->slot); + if (sas_device->enclosure_handle != 0) + sdev_printk(KERN_INFO, sdev, + "%s: enclosure_logical_id(0x%016llx), slot(%d)\n", + ds, (unsigned long long) + sas_device->enclosure_logical_id, sas_device->slot); + if (sas_device->connector_name[0] != '\0') + sdev_printk(KERN_INFO, sdev, + "%s: enclosure level(0x%04x), connector name( %s)\n", + ds, sas_device->enclosure_level, + sas_device->connector_name); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -2189,10 +2235,17 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) sas_device->handle, (unsigned long long)sas_device->sas_address, sas_device->phy); - starget_printk(KERN_INFO, starget, - "enclosure_logical_id(0x%016llx), slot(%d)\n", - (unsigned long long)sas_device->enclosure_logical_id, - sas_device->slot); + if (sas_device->enclosure_handle != 0) + starget_printk(KERN_INFO, starget, + "enclosure_logical_id(0x%016llx), slot(%d)\n", + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->connector_name) + starget_printk(KERN_INFO, starget, + "enclosure level(0x%04x),connector name(%s)\n", + sas_device->enclosure_level, + sas_device->connector_name); } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } @@ -2552,6 +2605,75 @@ _scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc) } /** + * _scsih_internal_device_block - block the sdev device + * @sdev: per device object + * @sas_device_priv_data : per device driver private data + * + * make sure device is blocked without error, if not + * print an error + */ +static void +_scsih_internal_device_block(struct scsi_device *sdev, + struct MPT3SAS_DEVICE *sas_device_priv_data) +{ + int r = 0; + + sdev_printk(KERN_INFO, sdev, "device_block, handle(0x%04x)\n", + sas_device_priv_data->sas_target->handle); + sas_device_priv_data->block = 1; + + r = scsi_internal_device_block(sdev); + if (r == -EINVAL) + sdev_printk(KERN_WARNING, sdev, + "device_block failed with return(%d) for handle(0x%04x)\n", + sas_device_priv_data->sas_target->handle, r); +} + +/** + * _scsih_internal_device_unblock - unblock the sdev device + * @sdev: per device object + * @sas_device_priv_data : per device driver private data + * make sure device is unblocked without error, if not retry + * by blocking and then unblocking + */ + +static void +_scsih_internal_device_unblock(struct scsi_device *sdev, + struct MPT3SAS_DEVICE *sas_device_priv_data) +{ + int r = 0; + + sdev_printk(KERN_WARNING, sdev, "device_unblock and setting to running, " + "handle(0x%04x)\n", sas_device_priv_data->sas_target->handle); + sas_device_priv_data->block = 0; + r = scsi_internal_device_unblock(sdev, SDEV_RUNNING); + if (r == -EINVAL) { + /* The device has been set to SDEV_RUNNING by SD layer during + * device addition but the request queue is still stopped by + * our earlier block call. We need to perform a block again + * to get the device to SDEV_BLOCK and then to SDEV_RUNNING */ + + sdev_printk(KERN_WARNING, sdev, + "device_unblock failed with return(%d) for handle(0x%04x) " + "performing a block followed by an unblock\n", + sas_device_priv_data->sas_target->handle, r); + sas_device_priv_data->block = 1; + r = scsi_internal_device_block(sdev); + if (r) + sdev_printk(KERN_WARNING, sdev, "retried device_block " + "failed with return(%d) for handle(0x%04x)\n", + sas_device_priv_data->sas_target->handle, r); + + sas_device_priv_data->block = 0; + r = scsi_internal_device_unblock(sdev, SDEV_RUNNING); + if (r) + sdev_printk(KERN_WARNING, sdev, "retried device_unblock" + " failed with return(%d) for handle(0x%04x)\n", + sas_device_priv_data->sas_target->handle, r); + } +} + +/** * _scsih_ublock_io_all_device - unblock every device * @ioc: per adapter object * @@ -2570,11 +2692,10 @@ _scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc) if (!sas_device_priv_data->block) continue; - sas_device_priv_data->block = 0; dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, "device_running, handle(0x%04x)\n", sas_device_priv_data->sas_target->handle)); - scsi_internal_device_unblock(sdev, SDEV_RUNNING); + _scsih_internal_device_unblock(sdev, sas_device_priv_data); } } @@ -2599,10 +2720,9 @@ _scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address) if (sas_device_priv_data->sas_target->sas_address != sas_address) continue; - if (sas_device_priv_data->block) { - sas_device_priv_data->block = 0; - scsi_internal_device_unblock(sdev, SDEV_RUNNING); - } + if (sas_device_priv_data->block) + _scsih_internal_device_unblock(sdev, + sas_device_priv_data); } } @@ -2625,10 +2745,7 @@ _scsih_block_io_all_device(struct MPT3SAS_ADAPTER *ioc) continue; if (sas_device_priv_data->block) continue; - sas_device_priv_data->block = 1; - scsi_internal_device_block(sdev); - sdev_printk(KERN_INFO, sdev, "device_blocked, handle(0x%04x)\n", - sas_device_priv_data->sas_target->handle); + _scsih_internal_device_block(sdev, sas_device_priv_data); } } @@ -2644,6 +2761,11 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) { struct MPT3SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev; + struct _sas_device *sas_device; + + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (!sas_device) + return; shost_for_each_device(sdev, ioc->shost) { sas_device_priv_data = sdev->hostdata; @@ -2653,10 +2775,9 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) continue; if (sas_device_priv_data->block) continue; - sas_device_priv_data->block = 1; - scsi_internal_device_block(sdev); - sdev_printk(KERN_INFO, sdev, - "device_blocked, handle(0x%04x)\n", handle); + if (sas_device->pend_sas_rphy_add) + continue; + _scsih_internal_device_block(sdev, sas_device_priv_data); } } @@ -2806,6 +2927,18 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) "setting delete flag: handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, handle, (unsigned long long)sas_address)); + if (sas_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "setting delete flag:enclosure logical id(0x%016llx)," + " slot(%d)\n", ioc->name, (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot)); + if (sas_device->connector_name) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "setting delete flag: enclosure level(0x%04x)," + " connector name( %s)\n", ioc->name, + sas_device->enclosure_level, + sas_device->connector_name)); _scsih_ublock_io_device(ioc, sas_address); sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; } @@ -3821,10 +3954,19 @@ _scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, "\tsas_address(0x%016llx), phy(%d)\n", ioc->name, (unsigned long long) sas_device->sas_address, sas_device->phy); - pr_warn(MPT3SAS_FMT - "\tenclosure_logical_id(0x%016llx), slot(%d)\n", - ioc->name, (unsigned long long) - sas_device->enclosure_logical_id, sas_device->slot); + if (sas_device->enclosure_handle != 0) + pr_warn(MPT3SAS_FMT + "\tenclosure_logical_id(0x%016llx)," + "slot(%d)\n", ioc->name, + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->connector_name[0]) + pr_warn(MPT3SAS_FMT + "\tenclosure level(0x%04x)," + " connector name( %s)\n", ioc->name, + sas_device->enclosure_level, + sas_device->connector_name); } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } @@ -3999,7 +4141,16 @@ _scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle) spin_unlock_irqrestore(&ioc->sas_device_lock, flags); return; } - starget_printk(KERN_WARNING, starget, "predicted fault\n"); + if (sas_device->enclosure_handle != 0) + starget_printk(KERN_INFO, starget, "predicted fault, " + "enclosure logical id(0x%016llx), slot(%d)\n", + (unsigned long long)sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->connector_name[0] != '\0') + starget_printk(KERN_WARNING, starget, "predicted fault, " + "enclosure level(0x%04x), connector name( %s)\n", + sas_device->enclosure_level, + sas_device->connector_name); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) @@ -4119,8 +4270,15 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) _scsih_smart_predicted_fault(ioc, le16_to_cpu(mpi_reply->DevHandle)); mpt3sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq); - } +#ifdef CONFIG_SCSI_MPT3SAS_LOGGING + if (!(ioc->logging_level & MPT_DEBUG_REPLY) && + ((scmd->sense_buffer[2] == UNIT_ATTENTION) || + (scmd->sense_buffer[2] == MEDIUM_ERROR) || + (scmd->sense_buffer[2] == HARDWARE_ERROR))) + _scsih_scsi_ioc_info(ioc, scmd, mpi_reply, smid); +#endif + } switch (ioc_status) { case MPI2_IOCSTATUS_BUSY: case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: @@ -4146,6 +4304,9 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) scmd->device->expecting_cc_ua = 1; } break; + } else if (log_info == VIRTUAL_IO_FAILED_RETRY) { + scmd->result = DID_RESET << 16; + break; } scmd->result = DID_SOFT_ERROR << 16; break; @@ -4788,6 +4949,16 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, sas_device->handle, handle); sas_target_priv_data->handle = handle; sas_device->handle = handle; + if (sas_device_pg0.Flags & + MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { + sas_device->enclosure_level = + le16_to_cpu(sas_device_pg0.EnclosureLevel); + memcpy(&sas_device->connector_name[0], + &sas_device_pg0.ConnectorName[0], 4); + } else { + sas_device->enclosure_level = 0; + sas_device->connector_name[0] = '\0'; + } } /* check if device is present */ @@ -4894,14 +5065,24 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, ioc->name, __FILE__, __LINE__, __func__); sas_device->enclosure_handle = le16_to_cpu(sas_device_pg0.EnclosureHandle); - sas_device->slot = - le16_to_cpu(sas_device_pg0.Slot); + if (sas_device->enclosure_handle != 0) + sas_device->slot = + le16_to_cpu(sas_device_pg0.Slot); sas_device->device_info = device_info; sas_device->sas_address = sas_address; sas_device->phy = sas_device_pg0.PhyNum; sas_device->fast_path = (le16_to_cpu(sas_device_pg0.Flags) & MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) ? 1 : 0; + if (sas_device_pg0.Flags & MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { + sas_device->enclosure_level = + le16_to_cpu(sas_device_pg0.EnclosureLevel); + memcpy(&sas_device->connector_name[0], + &sas_device_pg0.ConnectorName[0], 4); + } else { + sas_device->enclosure_level = 0; + sas_device->connector_name[0] = '\0'; + } /* get enclosure_logical_id */ if (sas_device->enclosure_handle && !(mpt3sas_config_get_enclosure_pg0( ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, @@ -4943,6 +5124,18 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, ioc->name, __func__, sas_device->handle, (unsigned long long) sas_device->sas_address)); + if (sas_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enter: enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, __func__, + (unsigned long long)sas_device->enclosure_logical_id, + sas_device->slot)); + if (sas_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enter: enclosure level(0x%04x), connector name( %s)\n", + ioc->name, __func__, + sas_device->enclosure_level, + sas_device->connector_name)); if (sas_device->starget && sas_device->starget->hostdata) { sas_target_priv_data = sas_device->starget->hostdata; @@ -4959,12 +5152,34 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, "removing handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, sas_device->handle, (unsigned long long) sas_device->sas_address); + if (sas_device->enclosure_handle != 0) + pr_info(MPT3SAS_FMT + "removing : enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, + (unsigned long long)sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->connector_name[0] != '\0') + pr_info(MPT3SAS_FMT + "removing enclosure level(0x%04x), connector name( %s)\n", + ioc->name, sas_device->enclosure_level, + sas_device->connector_name); dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: exit: handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, - sas_device->handle, (unsigned long long) - sas_device->sas_address)); + sas_device->handle, (unsigned long long) + sas_device->sas_address)); + if (sas_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: exit: enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, __func__, + (unsigned long long)sas_device->enclosure_logical_id, + sas_device->slot)); + if (sas_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: exit: enclosure level(0x%04x), connector name(%s)\n", + ioc->name, __func__, sas_device->enclosure_level, + sas_device->connector_name)); kfree(sas_device); } @@ -6357,9 +6572,7 @@ _scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc) /** * _scsih_mark_responding_sas_device - mark a sas_devices as responding * @ioc: per adapter object - * @sas_address: sas address - * @slot: enclosure slot id - * @handle: device handle + * @sas_device_pg0: SAS Device page 0 * * After host reset, find out whether devices are still responding. * Used in _scsih_remove_unresponsive_sas_devices. @@ -6367,8 +6580,8 @@ _scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc) * Return nothing. */ static void -_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, - u16 slot, u16 handle) +_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, +Mpi2SasDevicePage0_t *sas_device_pg0) { struct MPT3SAS_TARGET *sas_target_priv_data = NULL; struct scsi_target *starget; @@ -6377,8 +6590,8 @@ _scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, spin_lock_irqsave(&ioc->sas_device_lock, flags); list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if (sas_device->sas_address == sas_address && - sas_device->slot == slot) { + if ((sas_device->sas_address == sas_device_pg0->SASAddress) && + (sas_device->slot == sas_device_pg0->Slot)) { sas_device->responding = 1; starget = sas_device->starget; if (starget && starget->hostdata) { @@ -6387,22 +6600,40 @@ _scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, sas_target_priv_data->deleted = 0; } else sas_target_priv_data = NULL; - if (starget) + if (starget) { starget_printk(KERN_INFO, starget, - "handle(0x%04x), sas_addr(0x%016llx), " - "enclosure logical id(0x%016llx), " - "slot(%d)\n", handle, - (unsigned long long)sas_device->sas_address, + "handle(0x%04x), sas_addr(0x%016llx)\n", + sas_device_pg0->DevHandle, (unsigned long long) - sas_device->enclosure_logical_id, - sas_device->slot); - if (sas_device->handle == handle) + sas_device->sas_address); + + if (sas_device->enclosure_handle != 0) + starget_printk(KERN_INFO, starget, + "enclosure logical id(0x%016llx)," + " slot(%d)\n", + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + } + if (sas_device_pg0->Flags & + MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { + sas_device->enclosure_level = + le16_to_cpu(sas_device_pg0->EnclosureLevel); + memcpy(&sas_device->connector_name[0], + &sas_device_pg0->ConnectorName[0], 4); + } else { + sas_device->enclosure_level = 0; + sas_device->connector_name[0] = '\0'; + } + + if (sas_device->handle == sas_device_pg0->DevHandle) goto out; pr_info("\thandle changed from(0x%04x)!!!\n", sas_device->handle); - sas_device->handle = handle; + sas_device->handle = sas_device_pg0->DevHandle; if (sas_target_priv_data) - sas_target_priv_data->handle = handle; + sas_target_priv_data->handle = + sas_device_pg0->DevHandle; goto out; } } @@ -6441,13 +6672,15 @@ _scsih_search_responding_sas_devices(struct MPT3SAS_ADAPTER *ioc) MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) break; - handle = le16_to_cpu(sas_device_pg0.DevHandle); + handle = sas_device_pg0.DevHandle = + le16_to_cpu(sas_device_pg0.DevHandle); device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); if (!(_scsih_is_end_device(device_info))) continue; - _scsih_mark_responding_sas_device(ioc, - le64_to_cpu(sas_device_pg0.SASAddress), - le16_to_cpu(sas_device_pg0.Slot), handle); + sas_device_pg0.SASAddress = + le64_to_cpu(sas_device_pg0.SASAddress); + sas_device_pg0.Slot = le16_to_cpu(sas_device_pg0.Slot); + _scsih_mark_responding_sas_device(ioc, &sas_device_pg0); } out: @@ -7854,8 +8087,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* event thread */ snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name), "fw_event%d", ioc->id); - ioc->firmware_event_thread = create_singlethread_workqueue( - ioc->firmware_event_name); + ioc->firmware_event_thread = alloc_ordered_workqueue( + ioc->firmware_event_name, WQ_MEM_RECLAIM); if (!ioc->firmware_event_thread) { pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); diff --git a/drivers/scsi/mpt3sas/mpt3sas_transport.c b/drivers/scsi/mpt3sas/mpt3sas_transport.c index efb98afc46e0..70fd019e7ee5 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_transport.c +++ b/drivers/scsi/mpt3sas/mpt3sas_transport.c @@ -649,6 +649,7 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, unsigned long flags; struct _sas_node *sas_node; struct sas_rphy *rphy; + struct _sas_device *sas_device = NULL; int i; struct sas_port *port; @@ -731,10 +732,27 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, mpt3sas_port->remote_identify.device_type); rphy->identify = mpt3sas_port->remote_identify; + + if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) { + sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, + mpt3sas_port->remote_identify.sas_address); + if (!sas_device) { + dfailprintk(ioc, printk(MPT3SAS_FMT + "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__)); + goto out_fail; + } + sas_device->pend_sas_rphy_add = 1; + } + if ((sas_rphy_add(rphy))) { pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); } + + if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) + sas_device->pend_sas_rphy_add = 0; + if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) dev_printk(KERN_INFO, &rphy->dev, "add: handle(0x%04x), sas_addr(0x%016llx)\n", @@ -1946,7 +1964,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, } else { dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio), blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL); - if (!dma_addr_out) { + if (pci_dma_mapping_error(ioc->pdev, dma_addr_out)) { pr_info(MPT3SAS_FMT "%s(): DMA Addr out = NULL\n", ioc->name, __func__); rc = -ENOMEM; @@ -1968,7 +1986,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, } else { dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio), blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); - if (!dma_addr_in) { + if (pci_dma_mapping_error(ioc->pdev, dma_addr_in)) { pr_info(MPT3SAS_FMT "%s(): DMA Addr in = NULL\n", ioc->name, __func__); rc = -ENOMEM; diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 39306b1e704c..04e67a190652 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -2642,6 +2642,7 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb) ts->resp = SAS_TASK_COMPLETE; ts->stat = SAS_OPEN_REJECT; ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; default: PM8001_IO_DBG(pm8001_ha, pm8001_printk("Unknown status 0x%x\n", status)); diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 0e1628f2018e..9a389f1508de 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -2337,6 +2337,7 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb) ts->resp = SAS_TASK_COMPLETE; ts->stat = SAS_OPEN_REJECT; ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; default: PM8001_IO_DBG(pm8001_ha, pm8001_printk("Unknown status 0x%x\n", status)); diff --git a/drivers/scsi/qla2xxx/Kconfig b/drivers/scsi/qla2xxx/Kconfig index 33f60c92e20e..a0f732b138e4 100644 --- a/drivers/scsi/qla2xxx/Kconfig +++ b/drivers/scsi/qla2xxx/Kconfig @@ -32,10 +32,10 @@ config SCSI_QLA_FC They are also included in the linux-firmware tree as well. config TCM_QLA2XXX - tristate "TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs" + tristate "TCM_QLA2XXX fabric module for QLogic 24xx+ series target mode HBAs" depends on SCSI_QLA_FC && TARGET_CORE depends on LIBFC select BTREE default n ---help--- - Say Y here to enable the TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs + Say Y here to enable the TCM_QLA2XXX fabric module for QLogic 24xx+ series target mode HBAs diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 7ed7bae6172b..ac65cb7b4886 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -1359,9 +1359,7 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess) struct qla_hw_data *ha = tgt->ha; scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); struct se_session *se_sess; - struct se_node_acl *se_nacl; struct tcm_qla2xxx_lport *lport; - struct tcm_qla2xxx_nacl *nacl; BUG_ON(in_interrupt()); @@ -1371,8 +1369,6 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess) dump_stack(); return; } - se_nacl = se_sess->se_node_acl; - nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); lport = vha->vha_tgt.target_lport_ptr; if (!lport) { @@ -1680,7 +1676,6 @@ static int tcm_qla2xxx_lport_register_npiv_cb(struct scsi_qla_host *base_vha, (struct tcm_qla2xxx_lport *)target_lport_ptr; struct tcm_qla2xxx_lport *base_lport = (struct tcm_qla2xxx_lport *)base_vha->vha_tgt.target_lport_ptr; - struct tcm_qla2xxx_tpg *base_tpg; struct fc_vport_identifiers vport_id; if (!qla_tgt_mode_enabled(base_vha)) { @@ -1693,7 +1688,6 @@ static int tcm_qla2xxx_lport_register_npiv_cb(struct scsi_qla_host *base_vha, pr_err("qla2xxx base_lport or tpg_1 not available\n"); return -EPERM; } - base_tpg = base_lport->tpg_1; memset(&vport_id, 0, sizeof(vport_id)); vport_id.port_name = npiv_wwpn; @@ -1810,6 +1804,11 @@ static const struct target_core_fabric_ops tcm_qla2xxx_ops = { .module = THIS_MODULE, .name = "qla2xxx", .node_acl_size = sizeof(struct tcm_qla2xxx_nacl), + /* + * XXX: Limit assumes single page per scatter-gather-list entry. + * Current maximum is ~4.9 MB per se_cmd->t_data_sg with PAGE_SIZE=4096 + */ + .max_data_sg_nents = 1200, .get_fabric_name = tcm_qla2xxx_get_fabric_name, .tpg_get_wwn = tcm_qla2xxx_get_fabric_wwn, .tpg_get_tag = tcm_qla2xxx_get_tag, @@ -1958,7 +1957,7 @@ static void __exit tcm_qla2xxx_exit(void) tcm_qla2xxx_deregister_configfs(); } -MODULE_DESCRIPTION("TCM QLA2XXX series NPIV enabled fabric driver"); +MODULE_DESCRIPTION("TCM QLA24XX+ series NPIV enabled fabric driver"); MODULE_LICENSE("GPL"); module_init(tcm_qla2xxx_init); module_exit(tcm_qla2xxx_exit); diff --git a/drivers/scsi/scsi_common.c b/drivers/scsi/scsi_common.c index 2ff092252b76..c126966130ab 100644 --- a/drivers/scsi/scsi_common.c +++ b/drivers/scsi/scsi_common.c @@ -5,6 +5,8 @@ #include <linux/bug.h> #include <linux/kernel.h> #include <linux/string.h> +#include <linux/errno.h> +#include <asm/unaligned.h> #include <scsi/scsi_common.h> /* NB: These are exposed through /proc/scsi/scsi and form part of the ABI. @@ -176,3 +178,110 @@ bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len, return true; } EXPORT_SYMBOL(scsi_normalize_sense); + +/** + * scsi_sense_desc_find - search for a given descriptor type in descriptor sense data format. + * @sense_buffer: byte array of descriptor format sense data + * @sb_len: number of valid bytes in sense_buffer + * @desc_type: value of descriptor type to find + * (e.g. 0 -> information) + * + * Notes: + * only valid when sense data is in descriptor format + * + * Return value: + * pointer to start of (first) descriptor if found else NULL + */ +const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len, + int desc_type) +{ + int add_sen_len, add_len, desc_len, k; + const u8 * descp; + + if ((sb_len < 8) || (0 == (add_sen_len = sense_buffer[7]))) + return NULL; + if ((sense_buffer[0] < 0x72) || (sense_buffer[0] > 0x73)) + return NULL; + add_sen_len = (add_sen_len < (sb_len - 8)) ? + add_sen_len : (sb_len - 8); + descp = &sense_buffer[8]; + for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) { + descp += desc_len; + add_len = (k < (add_sen_len - 1)) ? descp[1]: -1; + desc_len = add_len + 2; + if (descp[0] == desc_type) + return descp; + if (add_len < 0) // short descriptor ?? + break; + } + return NULL; +} +EXPORT_SYMBOL(scsi_sense_desc_find); + +/** + * scsi_build_sense_buffer - build sense data in a buffer + * @desc: Sense format (non zero == descriptor format, + * 0 == fixed format) + * @buf: Where to build sense data + * @key: Sense key + * @asc: Additional sense code + * @ascq: Additional sense code qualifier + * + **/ +void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq) +{ + if (desc) { + buf[0] = 0x72; /* descriptor, current */ + buf[1] = key; + buf[2] = asc; + buf[3] = ascq; + buf[7] = 0; + } else { + buf[0] = 0x70; /* fixed, current */ + buf[2] = key; + buf[7] = 0xa; + buf[12] = asc; + buf[13] = ascq; + } +} +EXPORT_SYMBOL(scsi_build_sense_buffer); + +/** + * scsi_set_sense_information - set the information field in a + * formatted sense data buffer + * @buf: Where to build sense data + * @buf_len: buffer length + * @info: 64-bit information value to be set + * + * Return value: + * 0 on success or EINVAL for invalid sense buffer length + **/ +int scsi_set_sense_information(u8 *buf, int buf_len, u64 info) +{ + if ((buf[0] & 0x7f) == 0x72) { + u8 *ucp, len; + + len = buf[7]; + ucp = (char *)scsi_sense_desc_find(buf, len + 8, 0); + if (!ucp) { + buf[7] = len + 0xc; + ucp = buf + 8 + len; + } + + if (buf_len < len + 0xc) + /* Not enough room for info */ + return -EINVAL; + + ucp[0] = 0; + ucp[1] = 0xa; + ucp[2] = 0x80; /* Valid bit */ + ucp[3] = 0; + put_unaligned_be64(info, &ucp[4]); + } else if ((buf[0] & 0x7f) == 0x70) { + buf[0] |= 0x80; + put_unaligned_be64(info, &buf[3]); + } + + return 0; +} +EXPORT_SYMBOL(scsi_set_sense_information); diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 30268bb2ddb6..dfcc45bb03b1 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -25,6 +25,9 @@ * module options to "modprobe scsi_debug num_tgts=2" [20021221] */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ + #include <linux/module.h> #include <linux/kernel.h> @@ -201,7 +204,6 @@ static const char *scsi_debug_version_date = "20141022"; /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1) * or "peripheral device" addressing (value 0) */ #define SAM2_LUN_ADDRESS_METHOD 0 -#define SAM2_WLUN_REPORT_LUNS 0xc101 /* SCSI_DEBUG_CANQUEUE is the maximum number of commands that can be queued * (for response) at one time. Can be reduced by max_queue option. Command @@ -698,7 +700,7 @@ static void sdebug_max_tgts_luns(void) else hpnt->max_id = scsi_debug_num_tgts; /* scsi_debug_max_luns; */ - hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; + hpnt->max_lun = SCSI_W_LUN_REPORT_LUNS + 1; } spin_unlock(&sdebug_host_list_lock); } @@ -1288,7 +1290,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC); if (! arr) return DID_REQUEUE << 16; - have_wlun = (scp->device->lun == SAM2_WLUN_REPORT_LUNS); + have_wlun = (scp->device->lun == SCSI_W_LUN_REPORT_LUNS); if (have_wlun) pq_pdt = 0x1e; /* present, wlun */ else if (scsi_debug_no_lun_0 && (0 == devip->lun)) @@ -1427,12 +1429,11 @@ static int resp_requests(struct scsi_cmnd * scp, unsigned char * sbuff; unsigned char *cmd = scp->cmnd; unsigned char arr[SCSI_SENSE_BUFFERSIZE]; - bool dsense, want_dsense; + bool dsense; int len = 18; memset(arr, 0, sizeof(arr)); dsense = !!(cmd[1] & 1); - want_dsense = dsense || scsi_debug_dsense; sbuff = scp->sense_buffer; if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) { if (dsense) { @@ -2446,8 +2447,7 @@ static int dif_verify(struct sd_dif_tuple *sdt, const void *data, __be16 csum = dif_compute_csum(data, scsi_debug_sector_size); if (sdt->guard_tag != csum) { - pr_err("%s: GUARD check failed on sector %lu rcvd 0x%04x, data 0x%04x\n", - __func__, + pr_err("GUARD check failed on sector %lu rcvd 0x%04x, data 0x%04x\n", (unsigned long)sector, be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum)); @@ -2455,14 +2455,14 @@ static int dif_verify(struct sd_dif_tuple *sdt, const void *data, } if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION && be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) { - pr_err("%s: REF check failed on sector %lu\n", - __func__, (unsigned long)sector); + pr_err("REF check failed on sector %lu\n", + (unsigned long)sector); return 0x03; } if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && be32_to_cpu(sdt->ref_tag) != ei_lba) { - pr_err("%s: REF check failed on sector %lu\n", - __func__, (unsigned long)sector); + pr_err("REF check failed on sector %lu\n", + (unsigned long)sector); return 0x03; } return 0; @@ -2680,7 +2680,7 @@ resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) return 0; } -void dump_sector(unsigned char *buf, int len) +static void dump_sector(unsigned char *buf, int len) { int i, j, n; @@ -3365,8 +3365,8 @@ static int resp_report_luns(struct scsi_cmnd * scp, one_lun[i].scsi_lun[1] = lun & 0xff; } if (want_wlun) { - one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff; - one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff; + one_lun[i].scsi_lun[0] = (SCSI_W_LUN_REPORT_LUNS >> 8) & 0xff; + one_lun[i].scsi_lun[1] = SCSI_W_LUN_REPORT_LUNS & 0xff; i++; } alloc_len = (unsigned char *)(one_lun + i) - arr; @@ -3449,7 +3449,7 @@ static void sdebug_q_cmd_complete(unsigned long indx) atomic_inc(&sdebug_completions); qa_indx = indx; if ((qa_indx < 0) || (qa_indx >= SCSI_DEBUG_CANQUEUE)) { - pr_err("%s: wild qa_indx=%d\n", __func__, qa_indx); + pr_err("wild qa_indx=%d\n", qa_indx); return; } spin_lock_irqsave(&queued_arr_lock, iflags); @@ -3457,21 +3457,21 @@ static void sdebug_q_cmd_complete(unsigned long indx) scp = sqcp->a_cmnd; if (NULL == scp) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: scp is NULL\n", __func__); + pr_err("scp is NULL\n"); return; } devip = (struct sdebug_dev_info *)scp->device->hostdata; if (devip) atomic_dec(&devip->num_in_q); else - pr_err("%s: devip=NULL\n", __func__); + pr_err("devip=NULL\n"); if (atomic_read(&retired_max_queue) > 0) retiring = 1; sqcp->a_cmnd = NULL; if (!test_and_clear_bit(qa_indx, queued_in_use_bm)) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: Unexpected completion\n", __func__); + pr_err("Unexpected completion\n"); return; } @@ -3481,7 +3481,7 @@ static void sdebug_q_cmd_complete(unsigned long indx) retval = atomic_read(&retired_max_queue); if (qa_indx >= retval) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: index %d too large\n", __func__, retval); + pr_err("index %d too large\n", retval); return; } k = find_last_bit(queued_in_use_bm, retval); @@ -3509,7 +3509,7 @@ sdebug_q_cmd_hrt_complete(struct hrtimer *timer) atomic_inc(&sdebug_completions); qa_indx = sd_hrtp->qa_indx; if ((qa_indx < 0) || (qa_indx >= SCSI_DEBUG_CANQUEUE)) { - pr_err("%s: wild qa_indx=%d\n", __func__, qa_indx); + pr_err("wild qa_indx=%d\n", qa_indx); goto the_end; } spin_lock_irqsave(&queued_arr_lock, iflags); @@ -3517,21 +3517,21 @@ sdebug_q_cmd_hrt_complete(struct hrtimer *timer) scp = sqcp->a_cmnd; if (NULL == scp) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: scp is NULL\n", __func__); + pr_err("scp is NULL\n"); goto the_end; } devip = (struct sdebug_dev_info *)scp->device->hostdata; if (devip) atomic_dec(&devip->num_in_q); else - pr_err("%s: devip=NULL\n", __func__); + pr_err("devip=NULL\n"); if (atomic_read(&retired_max_queue) > 0) retiring = 1; sqcp->a_cmnd = NULL; if (!test_and_clear_bit(qa_indx, queued_in_use_bm)) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: Unexpected completion\n", __func__); + pr_err("Unexpected completion\n"); goto the_end; } @@ -3541,7 +3541,7 @@ sdebug_q_cmd_hrt_complete(struct hrtimer *timer) retval = atomic_read(&retired_max_queue); if (qa_indx >= retval) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: index %d too large\n", __func__, retval); + pr_err("index %d too large\n", retval); goto the_end; } k = find_last_bit(queued_in_use_bm, retval); @@ -3580,7 +3580,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) return devip; sdbg_host = *(struct sdebug_host_info **)shost_priv(sdev->host); if (!sdbg_host) { - pr_err("%s: Host info NULL\n", __func__); + pr_err("Host info NULL\n"); return NULL; } list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) { @@ -3596,8 +3596,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) if (!open_devip) { /* try and make a new one */ open_devip = sdebug_device_create(sdbg_host, GFP_ATOMIC); if (!open_devip) { - printk(KERN_ERR "%s: out of memory at line %d\n", - __func__, __LINE__); + pr_err("out of memory at line %d\n", __LINE__); return NULL; } } @@ -3615,7 +3614,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) static int scsi_debug_slave_alloc(struct scsi_device *sdp) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %llu>\n", + pr_info("slave_alloc <%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); queue_flag_set_unlocked(QUEUE_FLAG_BIDI, sdp->request_queue); return 0; @@ -3626,7 +3625,7 @@ static int scsi_debug_slave_configure(struct scsi_device *sdp) struct sdebug_dev_info *devip; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %llu>\n", + pr_info("slave_configure <%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN) sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN; @@ -3646,7 +3645,7 @@ static void scsi_debug_slave_destroy(struct scsi_device *sdp) (struct sdebug_dev_info *)sdp->hostdata; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %llu>\n", + pr_info("slave_destroy <%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (devip) { /* make this slot available for re-use */ @@ -3897,8 +3896,7 @@ static void __init sdebug_build_parts(unsigned char *ramp, return; if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) { scsi_debug_num_parts = SDEBUG_MAX_PARTS; - pr_warn("%s: reducing partitions to %d\n", __func__, - SDEBUG_MAX_PARTS); + pr_warn("reducing partitions to %d\n", SDEBUG_MAX_PARTS); } num_sectors = (int)sdebug_store_sectors; sectors_per_part = (num_sectors - sdebug_sectors_per) @@ -3942,14 +3940,20 @@ schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip, unsigned long iflags; int k, num_in_q, qdepth, inject; struct sdebug_queued_cmd *sqcp = NULL; - struct scsi_device *sdp = cmnd->device; + struct scsi_device *sdp; + + /* this should never happen */ + if (WARN_ON(!cmnd)) + return SCSI_MLQUEUE_HOST_BUSY; - if (NULL == cmnd || NULL == devip) { - pr_warn("%s: called with NULL cmnd or devip pointer\n", - __func__); + if (NULL == devip) { + pr_warn("called devip == NULL\n"); /* no particularly good error to report back */ return SCSI_MLQUEUE_HOST_BUSY; } + + sdp = cmnd->device; + if ((scsi_result) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) sdev_printk(KERN_INFO, sdp, "%s: non-zero result=0x%x\n", __func__, scsi_result); @@ -4383,8 +4387,7 @@ static ssize_t fake_rw_store(struct device_driver *ddp, const char *buf, fake_storep = vmalloc(sz); if (NULL == fake_storep) { - pr_err("%s: out of memory, 9\n", - __func__); + pr_err("out of memory, 9\n"); return -ENOMEM; } memset(fake_storep, 0, sz); @@ -4784,8 +4787,7 @@ static int __init scsi_debug_init(void) atomic_set(&retired_max_queue, 0); if (scsi_debug_ndelay >= 1000000000) { - pr_warn("%s: ndelay must be less than 1 second, ignored\n", - __func__); + pr_warn("ndelay must be less than 1 second, ignored\n"); scsi_debug_ndelay = 0; } else if (scsi_debug_ndelay > 0) scsi_debug_delay = DELAY_OVERRIDDEN; @@ -4797,8 +4799,7 @@ static int __init scsi_debug_init(void) case 4096: break; default: - pr_err("%s: invalid sector_size %d\n", __func__, - scsi_debug_sector_size); + pr_err("invalid sector_size %d\n", scsi_debug_sector_size); return -EINVAL; } @@ -4811,29 +4812,28 @@ static int __init scsi_debug_init(void) break; default: - pr_err("%s: dif must be 0, 1, 2 or 3\n", __func__); + pr_err("dif must be 0, 1, 2 or 3\n"); return -EINVAL; } if (scsi_debug_guard > 1) { - pr_err("%s: guard must be 0 or 1\n", __func__); + pr_err("guard must be 0 or 1\n"); return -EINVAL; } if (scsi_debug_ato > 1) { - pr_err("%s: ato must be 0 or 1\n", __func__); + pr_err("ato must be 0 or 1\n"); return -EINVAL; } if (scsi_debug_physblk_exp > 15) { - pr_err("%s: invalid physblk_exp %u\n", __func__, - scsi_debug_physblk_exp); + pr_err("invalid physblk_exp %u\n", scsi_debug_physblk_exp); return -EINVAL; } if (scsi_debug_lowest_aligned > 0x3fff) { - pr_err("%s: lowest_aligned too big: %u\n", __func__, - scsi_debug_lowest_aligned); + pr_err("lowest_aligned too big: %u\n", + scsi_debug_lowest_aligned); return -EINVAL; } @@ -4863,7 +4863,7 @@ static int __init scsi_debug_init(void) if (0 == scsi_debug_fake_rw) { fake_storep = vmalloc(sz); if (NULL == fake_storep) { - pr_err("%s: out of memory, 1\n", __func__); + pr_err("out of memory, 1\n"); return -ENOMEM; } memset(fake_storep, 0, sz); @@ -4877,11 +4877,10 @@ static int __init scsi_debug_init(void) dif_size = sdebug_store_sectors * sizeof(struct sd_dif_tuple); dif_storep = vmalloc(dif_size); - pr_err("%s: dif_storep %u bytes @ %p\n", __func__, dif_size, - dif_storep); + pr_err("dif_storep %u bytes @ %p\n", dif_size, dif_storep); if (dif_storep == NULL) { - pr_err("%s: out of mem. (DIX)\n", __func__); + pr_err("out of mem. (DIX)\n"); ret = -ENOMEM; goto free_vm; } @@ -4903,18 +4902,17 @@ static int __init scsi_debug_init(void) if (scsi_debug_unmap_alignment && scsi_debug_unmap_granularity <= scsi_debug_unmap_alignment) { - pr_err("%s: ERR: unmap_granularity <= unmap_alignment\n", - __func__); + pr_err("ERR: unmap_granularity <= unmap_alignment\n"); return -EINVAL; } map_size = lba_to_map_index(sdebug_store_sectors - 1) + 1; map_storep = vmalloc(BITS_TO_LONGS(map_size) * sizeof(long)); - pr_info("%s: %lu provisioning blocks\n", __func__, map_size); + pr_info("%lu provisioning blocks\n", map_size); if (map_storep == NULL) { - pr_err("%s: out of mem. (MAP)\n", __func__); + pr_err("out of mem. (MAP)\n"); ret = -ENOMEM; goto free_vm; } @@ -4928,18 +4926,18 @@ static int __init scsi_debug_init(void) pseudo_primary = root_device_register("pseudo_0"); if (IS_ERR(pseudo_primary)) { - pr_warn("%s: root_device_register() error\n", __func__); + pr_warn("root_device_register() error\n"); ret = PTR_ERR(pseudo_primary); goto free_vm; } ret = bus_register(&pseudo_lld_bus); if (ret < 0) { - pr_warn("%s: bus_register error: %d\n", __func__, ret); + pr_warn("bus_register error: %d\n", ret); goto dev_unreg; } ret = driver_register(&sdebug_driverfs_driver); if (ret < 0) { - pr_warn("%s: driver_register error: %d\n", __func__, ret); + pr_warn("driver_register error: %d\n", ret); goto bus_unreg; } @@ -4948,16 +4946,14 @@ static int __init scsi_debug_init(void) for (k = 0; k < host_to_add; k++) { if (sdebug_add_adapter()) { - pr_err("%s: sdebug_add_adapter failed k=%d\n", - __func__, k); + pr_err("sdebug_add_adapter failed k=%d\n", k); break; } } - if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { - pr_info("%s: built %d host(s)\n", __func__, - scsi_debug_add_host); - } + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + pr_info("built %d host(s)\n", scsi_debug_add_host); + return 0; bus_unreg: @@ -4965,10 +4961,8 @@ bus_unreg: dev_unreg: root_device_unregister(pseudo_primary); free_vm: - if (map_storep) - vfree(map_storep); - if (dif_storep) - vfree(dif_storep); + vfree(map_storep); + vfree(dif_storep); vfree(fake_storep); return ret; @@ -4986,9 +4980,7 @@ static void __exit scsi_debug_exit(void) bus_unregister(&pseudo_lld_bus); root_device_unregister(pseudo_primary); - if (dif_storep) - vfree(dif_storep); - + vfree(dif_storep); vfree(fake_storep); } @@ -5012,8 +5004,7 @@ static int sdebug_add_adapter(void) sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL); if (NULL == sdbg_host) { - printk(KERN_ERR "%s: out of memory at line %d\n", - __func__, __LINE__); + pr_err("out of memory at line %d\n", __LINE__); return -ENOMEM; } @@ -5023,8 +5014,7 @@ static int sdebug_add_adapter(void) for (k = 0; k < devs_per_host; k++) { sdbg_devinfo = sdebug_device_create(sdbg_host, GFP_KERNEL); if (!sdbg_devinfo) { - printk(KERN_ERR "%s: out of memory at line %d\n", - __func__, __LINE__); + pr_err("out of memory at line %d\n", __LINE__); error = -ENOMEM; goto clean; } @@ -5178,7 +5168,7 @@ scsi_debug_queuecommand(struct scsi_cmnd *scp) } sdev_printk(KERN_INFO, sdp, "%s: cmd %s\n", my_name, b); } - has_wlun_rl = (sdp->lun == SAM2_WLUN_REPORT_LUNS); + has_wlun_rl = (sdp->lun == SCSI_W_LUN_REPORT_LUNS); if ((sdp->lun >= scsi_debug_max_luns) && !has_wlun_rl) return schedule_resp(scp, NULL, errsts_no_connect, 0); @@ -5338,7 +5328,7 @@ static int sdebug_driver_probe(struct device * dev) sdebug_driver_template.use_clustering = ENABLE_CLUSTERING; hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host)); if (NULL == hpnt) { - pr_err("%s: scsi_host_alloc failed\n", __func__); + pr_err("scsi_host_alloc failed\n"); error = -ENODEV; return error; } @@ -5349,7 +5339,8 @@ static int sdebug_driver_probe(struct device * dev) hpnt->max_id = scsi_debug_num_tgts + 1; else hpnt->max_id = scsi_debug_num_tgts; - hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; /* = scsi_debug_max_luns; */ + /* = scsi_debug_max_luns; */ + hpnt->max_lun = SCSI_W_LUN_REPORT_LUNS + 1; host_prot = 0; @@ -5381,7 +5372,7 @@ static int sdebug_driver_probe(struct device * dev) scsi_host_set_prot(hpnt, host_prot); - printk(KERN_INFO "scsi_debug: host protection%s%s%s%s%s%s%s\n", + pr_info("host protection%s%s%s%s%s%s%s\n", (host_prot & SHOST_DIF_TYPE1_PROTECTION) ? " DIF1" : "", (host_prot & SHOST_DIF_TYPE2_PROTECTION) ? " DIF2" : "", (host_prot & SHOST_DIF_TYPE3_PROTECTION) ? " DIF3" : "", @@ -5409,7 +5400,7 @@ static int sdebug_driver_probe(struct device * dev) error = scsi_add_host(hpnt, &sdbg_host->dev); if (error) { - printk(KERN_ERR "%s: scsi_add_host failed\n", __func__); + pr_err("scsi_add_host failed\n"); error = -ENODEV; scsi_host_put(hpnt); } else @@ -5426,8 +5417,7 @@ static int sdebug_driver_remove(struct device * dev) sdbg_host = to_sdebug_host(dev); if (!sdbg_host) { - printk(KERN_ERR "%s: Unable to locate host info\n", - __func__); + pr_err("Unable to locate host info\n"); return -ENODEV; } diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c new file mode 100644 index 000000000000..edb044a7b56d --- /dev/null +++ b/drivers/scsi/scsi_dh.c @@ -0,0 +1,437 @@ +/* + * SCSI device handler infrastruture. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright IBM Corporation, 2007 + * Authors: + * Chandra Seetharaman <sekharan@us.ibm.com> + * Mike Anderson <andmike@linux.vnet.ibm.com> + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <scsi/scsi_dh.h> +#include "scsi_priv.h" + +static DEFINE_SPINLOCK(list_lock); +static LIST_HEAD(scsi_dh_list); + +struct scsi_dh_blist { + const char *vendor; + const char *model; + const char *driver; +}; + +static const struct scsi_dh_blist scsi_dh_blist[] = { + {"DGC", "RAID", "clariion" }, + {"DGC", "DISK", "clariion" }, + {"DGC", "VRAID", "clariion" }, + + {"COMPAQ", "MSA1000 VOLUME", "hp_sw" }, + {"COMPAQ", "HSV110", "hp_sw" }, + {"HP", "HSV100", "hp_sw"}, + {"DEC", "HSG80", "hp_sw"}, + + {"IBM", "1722", "rdac", }, + {"IBM", "1724", "rdac", }, + {"IBM", "1726", "rdac", }, + {"IBM", "1742", "rdac", }, + {"IBM", "1745", "rdac", }, + {"IBM", "1746", "rdac", }, + {"IBM", "1813", "rdac", }, + {"IBM", "1814", "rdac", }, + {"IBM", "1815", "rdac", }, + {"IBM", "1818", "rdac", }, + {"IBM", "3526", "rdac", }, + {"SGI", "TP9", "rdac", }, + {"SGI", "IS", "rdac", }, + {"STK", "OPENstorage D280", "rdac", }, + {"STK", "FLEXLINE 380", "rdac", }, + {"SUN", "CSM", "rdac", }, + {"SUN", "LCSM100", "rdac", }, + {"SUN", "STK6580_6780", "rdac", }, + {"SUN", "SUN_6180", "rdac", }, + {"SUN", "ArrayStorage", "rdac", }, + {"DELL", "MD3", "rdac", }, + {"NETAPP", "INF-01-00", "rdac", }, + {"LSI", "INF-01-00", "rdac", }, + {"ENGENIO", "INF-01-00", "rdac", }, + {NULL, NULL, NULL }, +}; + +static const char * +scsi_dh_find_driver(struct scsi_device *sdev) +{ + const struct scsi_dh_blist *b; + + if (scsi_device_tpgs(sdev)) + return "alua"; + + for (b = scsi_dh_blist; b->vendor; b++) { + if (!strncmp(sdev->vendor, b->vendor, strlen(b->vendor)) && + !strncmp(sdev->model, b->model, strlen(b->model))) { + return b->driver; + } + } + return NULL; +} + + +static struct scsi_device_handler *__scsi_dh_lookup(const char *name) +{ + struct scsi_device_handler *tmp, *found = NULL; + + spin_lock(&list_lock); + list_for_each_entry(tmp, &scsi_dh_list, list) { + if (!strncmp(tmp->name, name, strlen(tmp->name))) { + found = tmp; + break; + } + } + spin_unlock(&list_lock); + return found; +} + +static struct scsi_device_handler *scsi_dh_lookup(const char *name) +{ + struct scsi_device_handler *dh; + + dh = __scsi_dh_lookup(name); + if (!dh) { + request_module(name); + dh = __scsi_dh_lookup(name); + } + + return dh; +} + +/* + * scsi_dh_handler_attach - Attach a device handler to a device + * @sdev - SCSI device the device handler should attach to + * @scsi_dh - The device handler to attach + */ +static int scsi_dh_handler_attach(struct scsi_device *sdev, + struct scsi_device_handler *scsi_dh) +{ + int error; + + if (!try_module_get(scsi_dh->module)) + return -EINVAL; + + error = scsi_dh->attach(sdev); + if (error) { + sdev_printk(KERN_ERR, sdev, "%s: Attach failed (%d)\n", + scsi_dh->name, error); + module_put(scsi_dh->module); + } else + sdev->handler = scsi_dh; + + return error; +} + +/* + * scsi_dh_handler_detach - Detach a device handler from a device + * @sdev - SCSI device the device handler should be detached from + */ +static void scsi_dh_handler_detach(struct scsi_device *sdev) +{ + sdev->handler->detach(sdev); + sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", sdev->handler->name); + module_put(sdev->handler->module); +} + +/* + * Functions for sysfs attribute 'dh_state' + */ +static ssize_t +store_dh_state(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct scsi_device_handler *scsi_dh; + int err = -EINVAL; + + if (sdev->sdev_state == SDEV_CANCEL || + sdev->sdev_state == SDEV_DEL) + return -ENODEV; + + if (!sdev->handler) { + /* + * Attach to a device handler + */ + scsi_dh = scsi_dh_lookup(buf); + if (!scsi_dh) + return err; + err = scsi_dh_handler_attach(sdev, scsi_dh); + } else { + if (!strncmp(buf, "detach", 6)) { + /* + * Detach from a device handler + */ + sdev_printk(KERN_WARNING, sdev, + "can't detach handler %s.\n", + sdev->handler->name); + err = -EINVAL; + } else if (!strncmp(buf, "activate", 8)) { + /* + * Activate a device handler + */ + if (sdev->handler->activate) + err = sdev->handler->activate(sdev, NULL, NULL); + else + err = 0; + } + } + + return err<0?err:count; +} + +static ssize_t +show_dh_state(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + + if (!sdev->handler) + return snprintf(buf, 20, "detached\n"); + + return snprintf(buf, 20, "%s\n", sdev->handler->name); +} + +static struct device_attribute scsi_dh_state_attr = + __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state, + store_dh_state); + +int scsi_dh_add_device(struct scsi_device *sdev) +{ + struct scsi_device_handler *devinfo = NULL; + const char *drv; + int err; + + err = device_create_file(&sdev->sdev_gendev, &scsi_dh_state_attr); + if (err) + return err; + + drv = scsi_dh_find_driver(sdev); + if (drv) + devinfo = scsi_dh_lookup(drv); + if (devinfo) + err = scsi_dh_handler_attach(sdev, devinfo); + return err; +} + +void scsi_dh_remove_device(struct scsi_device *sdev) +{ + if (sdev->handler) + scsi_dh_handler_detach(sdev); + device_remove_file(&sdev->sdev_gendev, &scsi_dh_state_attr); +} + +/* + * scsi_register_device_handler - register a device handler personality + * module. + * @scsi_dh - device handler to be registered. + * + * Returns 0 on success, -EBUSY if handler already registered. + */ +int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) +{ + if (__scsi_dh_lookup(scsi_dh->name)) + return -EBUSY; + + if (!scsi_dh->attach || !scsi_dh->detach) + return -EINVAL; + + spin_lock(&list_lock); + list_add(&scsi_dh->list, &scsi_dh_list); + spin_unlock(&list_lock); + + printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); + + return SCSI_DH_OK; +} +EXPORT_SYMBOL_GPL(scsi_register_device_handler); + +/* + * scsi_unregister_device_handler - register a device handler personality + * module. + * @scsi_dh - device handler to be unregistered. + * + * Returns 0 on success, -ENODEV if handler not registered. + */ +int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) +{ + if (!__scsi_dh_lookup(scsi_dh->name)) + return -ENODEV; + + spin_lock(&list_lock); + list_del(&scsi_dh->list); + spin_unlock(&list_lock); + printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); + + return SCSI_DH_OK; +} +EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); + +static struct scsi_device *get_sdev_from_queue(struct request_queue *q) +{ + struct scsi_device *sdev; + unsigned long flags; + + spin_lock_irqsave(q->queue_lock, flags); + sdev = q->queuedata; + if (!sdev || !get_device(&sdev->sdev_gendev)) + sdev = NULL; + spin_unlock_irqrestore(q->queue_lock, flags); + + return sdev; +} + +/* + * scsi_dh_activate - activate the path associated with the scsi_device + * corresponding to the given request queue. + * Returns immediately without waiting for activation to be completed. + * @q - Request queue that is associated with the scsi_device to be + * activated. + * @fn - Function to be called upon completion of the activation. + * Function fn is called with data (below) and the error code. + * Function fn may be called from the same calling context. So, + * do not hold the lock in the caller which may be needed in fn. + * @data - data passed to the function fn upon completion. + * + */ +int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) +{ + struct scsi_device *sdev; + int err = SCSI_DH_NOSYS; + + sdev = get_sdev_from_queue(q); + if (!sdev) { + if (fn) + fn(data, err); + return err; + } + + if (!sdev->handler) + goto out_fn; + err = SCSI_DH_NOTCONN; + if (sdev->sdev_state == SDEV_CANCEL || + sdev->sdev_state == SDEV_DEL) + goto out_fn; + + err = SCSI_DH_DEV_OFFLINED; + if (sdev->sdev_state == SDEV_OFFLINE) + goto out_fn; + + if (sdev->handler->activate) + err = sdev->handler->activate(sdev, fn, data); + +out_put_device: + put_device(&sdev->sdev_gendev); + return err; + +out_fn: + if (fn) + fn(data, err); + goto out_put_device; +} +EXPORT_SYMBOL_GPL(scsi_dh_activate); + +/* + * scsi_dh_set_params - set the parameters for the device as per the + * string specified in params. + * @q - Request queue that is associated with the scsi_device for + * which the parameters to be set. + * @params - parameters in the following format + * "no_of_params\0param1\0param2\0param3\0...\0" + * for example, string for 2 parameters with value 10 and 21 + * is specified as "2\010\021\0". + */ +int scsi_dh_set_params(struct request_queue *q, const char *params) +{ + struct scsi_device *sdev; + int err = -SCSI_DH_NOSYS; + + sdev = get_sdev_from_queue(q); + if (!sdev) + return err; + + if (sdev->handler && sdev->handler->set_params) + err = sdev->handler->set_params(sdev, params); + put_device(&sdev->sdev_gendev); + return err; +} +EXPORT_SYMBOL_GPL(scsi_dh_set_params); + +/* + * scsi_dh_attach - Attach device handler + * @q - Request queue that is associated with the scsi_device + * the handler should be attached to + * @name - name of the handler to attach + */ +int scsi_dh_attach(struct request_queue *q, const char *name) +{ + struct scsi_device *sdev; + struct scsi_device_handler *scsi_dh; + int err = 0; + + sdev = get_sdev_from_queue(q); + if (!sdev) + return -ENODEV; + + scsi_dh = scsi_dh_lookup(name); + if (!scsi_dh) { + err = -EINVAL; + goto out_put_device; + } + + if (sdev->handler) { + if (sdev->handler != scsi_dh) + err = -EBUSY; + goto out_put_device; + } + + err = scsi_dh_handler_attach(sdev, scsi_dh); + +out_put_device: + put_device(&sdev->sdev_gendev); + return err; +} +EXPORT_SYMBOL_GPL(scsi_dh_attach); + +/* + * scsi_dh_attached_handler_name - Get attached device handler's name + * @q - Request queue that is associated with the scsi_device + * that may have a device handler attached + * @gfp - the GFP mask used in the kmalloc() call when allocating memory + * + * Returns name of attached handler, NULL if no handler is attached. + * Caller must take care to free the returned string. + */ +const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) +{ + struct scsi_device *sdev; + const char *handler_name = NULL; + + sdev = get_sdev_from_queue(q); + if (!sdev) + return NULL; + + if (sdev->handler) + handler_name = kstrdup(sdev->handler->name, gfp); + put_device(&sdev->sdev_gendev); + return handler_name; +} +EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name); diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index afd34a608fe7..66a96cd98b97 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -33,9 +33,11 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_driver.h> #include <scsi/scsi_eh.h> +#include <scsi/scsi_common.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_host.h> #include <scsi/scsi_ioctl.h> +#include <scsi/scsi_dh.h> #include <scsi/sg.h> #include "scsi_priv.h" @@ -463,11 +465,10 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) if (scsi_sense_is_deferred(&sshdr)) return NEEDS_RETRY; - if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh && - sdev->scsi_dh_data->scsi_dh->check_sense) { + if (sdev->handler && sdev->handler->check_sense) { int rc; - rc = sdev->scsi_dh_data->scsi_dh->check_sense(sdev, &sshdr); + rc = sdev->handler->check_sense(sdev, &sshdr); if (rc != SCSI_RETURN_NOT_HANDLED) return rc; /* handler does not care. Drop down to default handling */ @@ -2178,8 +2179,17 @@ int scsi_error_handler(void *data) * We never actually get interrupted because kthread_run * disables signal delivery for the created thread. */ - while (!kthread_should_stop()) { + while (true) { + /* + * The sequence in kthread_stop() sets the stop flag first + * then wakes the process. To avoid missed wakeups, the task + * should always be in a non running state before the stop + * flag is checked + */ set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) + break; + if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) || shost->host_failed != atomic_read(&shost->host_busy)) { SCSI_LOG_ERROR_RECOVERY(1, @@ -2416,45 +2426,6 @@ bool scsi_command_normalize_sense(const struct scsi_cmnd *cmd, EXPORT_SYMBOL(scsi_command_normalize_sense); /** - * scsi_sense_desc_find - search for a given descriptor type in descriptor sense data format. - * @sense_buffer: byte array of descriptor format sense data - * @sb_len: number of valid bytes in sense_buffer - * @desc_type: value of descriptor type to find - * (e.g. 0 -> information) - * - * Notes: - * only valid when sense data is in descriptor format - * - * Return value: - * pointer to start of (first) descriptor if found else NULL - */ -const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len, - int desc_type) -{ - int add_sen_len, add_len, desc_len, k; - const u8 * descp; - - if ((sb_len < 8) || (0 == (add_sen_len = sense_buffer[7]))) - return NULL; - if ((sense_buffer[0] < 0x72) || (sense_buffer[0] > 0x73)) - return NULL; - add_sen_len = (add_sen_len < (sb_len - 8)) ? - add_sen_len : (sb_len - 8); - descp = &sense_buffer[8]; - for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) { - descp += desc_len; - add_len = (k < (add_sen_len - 1)) ? descp[1]: -1; - desc_len = add_len + 2; - if (descp[0] == desc_type) - return descp; - if (add_len < 0) // short descriptor ?? - break; - } - return NULL; -} -EXPORT_SYMBOL(scsi_sense_desc_find); - -/** * scsi_get_sense_info_fld - get information field from sense data (either fixed or descriptor format) * @sense_buffer: byte array of sense data * @sb_len: number of valid bytes in sense_buffer @@ -2503,31 +2474,3 @@ int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len, } } EXPORT_SYMBOL(scsi_get_sense_info_fld); - -/** - * scsi_build_sense_buffer - build sense data in a buffer - * @desc: Sense format (non zero == descriptor format, - * 0 == fixed format) - * @buf: Where to build sense data - * @key: Sense key - * @asc: Additional sense code - * @ascq: Additional sense code qualifier - * - **/ -void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq) -{ - if (desc) { - buf[0] = 0x72; /* descriptor, current */ - buf[1] = key; - buf[2] = asc; - buf[3] = ascq; - buf[7] = 0; - } else { - buf[0] = 0x70; /* fixed, current */ - buf[2] = key; - buf[7] = 0xa; - buf[12] = asc; - buf[13] = ascq; - } -} -EXPORT_SYMBOL(scsi_build_sense_buffer); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 882864f5cbae..cbfc5990052b 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -31,6 +31,7 @@ #include <scsi/scsi_driver.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_host.h> +#include <scsi/scsi_dh.h> #include <trace/events/scsi.h> @@ -1248,9 +1249,8 @@ static int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req) { struct scsi_cmnd *cmd = req->special; - if (unlikely(sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh - && sdev->scsi_dh_data->scsi_dh->prep_fn)) { - int ret = sdev->scsi_dh_data->scsi_dh->prep_fn(sdev, req); + if (unlikely(sdev->handler && sdev->handler->prep_fn)) { + int ret = sdev->handler->prep_fn(sdev, req); if (ret != BLKPREP_OK) return ret; } diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index e3902fc66278..644bb7339b55 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -170,6 +170,15 @@ static inline void scsi_autopm_put_host(struct Scsi_Host *h) {} extern struct async_domain scsi_sd_pm_domain; extern struct async_domain scsi_sd_probe_domain; +/* scsi_dh.c */ +#ifdef CONFIG_SCSI_DH +int scsi_dh_add_device(struct scsi_device *sdev); +void scsi_dh_remove_device(struct scsi_device *sdev); +#else +static inline int scsi_dh_add_device(struct scsi_device *sdev) { return 0; } +static inline void scsi_dh_remove_device(struct scsi_device *sdev) { } +#endif + /* * internal scsi timeout functions: for use by mid-layer and transport * classes. diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 9ad41168d26d..b333389f248f 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1030,11 +1030,20 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) "failed to add device: %d\n", error); return error; } + + error = scsi_dh_add_device(sdev); + if (error) { + sdev_printk(KERN_INFO, sdev, + "failed to add device handler: %d\n", error); + return error; + } + device_enable_async_suspend(&sdev->sdev_dev); error = device_add(&sdev->sdev_dev); if (error) { sdev_printk(KERN_INFO, sdev, "failed to add class device: %d\n", error); + scsi_dh_remove_device(sdev); device_del(&sdev->sdev_gendev); return error; } @@ -1074,6 +1083,7 @@ void __scsi_remove_device(struct scsi_device *sdev) bsg_unregister_queue(sdev->request_queue); device_unregister(&sdev->sdev_dev); transport_remove_device(dev); + scsi_dh_remove_device(sdev); device_del(dev); } else put_device(&sdev->sdev_dev); diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 9a058194b9bd..30d26e345dcc 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -1222,13 +1222,6 @@ show_sas_rphy_enclosure_identifier(struct device *dev, u64 identifier; int error; - /* - * Only devices behind an expander are supported, because the - * enclosure identifier is a SMP feature. - */ - if (scsi_is_sas_phy_local(phy)) - return -EINVAL; - error = i->f->get_enclosure_identifier(rphy, &identifier); if (error) return error; @@ -1248,9 +1241,6 @@ show_sas_rphy_bay_identifier(struct device *dev, struct sas_internal *i = to_sas_internal(shost->transportt); int val; - if (scsi_is_sas_phy_local(phy)) - return -EINVAL; - val = i->f->get_bay_identifier(rphy); if (val < 0) return val; diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c index 043419dcee92..8e72bcbd3d6d 100644 --- a/drivers/sh/intc/core.c +++ b/drivers/sh/intc/core.c @@ -65,7 +65,7 @@ void intc_set_prio_level(unsigned int irq, unsigned int level) raw_spin_unlock_irqrestore(&intc_big_lock, flags); } -static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) +static void intc_redirect_irq(struct irq_desc *desc) { generic_handle_irq((unsigned int)irq_desc_get_handler_data(desc)); } diff --git a/drivers/sh/intc/internals.h b/drivers/sh/intc/internals.h index 7dff08e2a071..6ce7f0d26dcf 100644 --- a/drivers/sh/intc/internals.h +++ b/drivers/sh/intc/internals.h @@ -99,15 +99,7 @@ static inline struct intc_desc_int *get_intc_desc(unsigned int irq) */ static inline void activate_irq(int irq) { -#ifdef CONFIG_ARM - /* ARM requires an extra step to clear IRQ_NOREQUEST, which it - * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE. - */ - set_irq_flags(irq, IRQF_VALID); -#else - /* same effect on other architectures */ - irq_set_noprobe(irq); -#endif + irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE); } static inline int intc_handle_int_cmp(const void *a, const void *b) diff --git a/drivers/sh/intc/virq.c b/drivers/sh/intc/virq.c index bafc51c6f0ba..e7899624aa0b 100644 --- a/drivers/sh/intc/virq.c +++ b/drivers/sh/intc/virq.c @@ -109,7 +109,7 @@ static int add_virq_to_pirq(unsigned int irq, unsigned int virq) return 0; } -static void intc_virq_handler(unsigned int __irq, struct irq_desc *desc) +static void intc_virq_handler(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct irq_data *data = irq_desc_get_irq_data(desc); @@ -127,7 +127,7 @@ static void intc_virq_handler(unsigned int __irq, struct irq_desc *desc) handle = (unsigned long)irq_desc_get_handler_data(vdesc); addr = INTC_REG(d, _INTC_ADDR_E(handle), 0); if (intc_reg_fns[_INTC_FN(handle)](addr, handle, 0)) - generic_handle_irq_desc(entry->irq, vdesc); + generic_handle_irq_desc(vdesc); } } diff --git a/drivers/sh/pm_runtime.c b/drivers/sh/pm_runtime.c index d3d1891cda3c..25abd4eb7d10 100644 --- a/drivers/sh/pm_runtime.c +++ b/drivers/sh/pm_runtime.c @@ -35,20 +35,11 @@ static struct pm_clk_notifier_block platform_bus_notifier = { static int __init sh_pm_runtime_init(void) { if (IS_ENABLED(CONFIG_ARCH_SHMOBILE_MULTI)) { - if (!of_machine_is_compatible("renesas,emev2") && - !of_machine_is_compatible("renesas,r7s72100") && -#ifndef CONFIG_PM_GENERIC_DOMAINS_OF - !of_machine_is_compatible("renesas,r8a73a4") && - !of_machine_is_compatible("renesas,r8a7740") && - !of_machine_is_compatible("renesas,sh73a0") && -#endif - !of_machine_is_compatible("renesas,r8a7778") && - !of_machine_is_compatible("renesas,r8a7779") && - !of_machine_is_compatible("renesas,r8a7790") && - !of_machine_is_compatible("renesas,r8a7791") && - !of_machine_is_compatible("renesas,r8a7792") && - !of_machine_is_compatible("renesas,r8a7793") && - !of_machine_is_compatible("renesas,r8a7794")) + if (!of_find_compatible_node(NULL, NULL, + "renesas,cpg-mstp-clocks")) + return 0; + if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS_OF) && + of_find_node_with_property(NULL, "#power-domain-cells")) return 0; } diff --git a/drivers/soc/dove/pmu.c b/drivers/soc/dove/pmu.c index 6792aae9e2e5..052aecf29893 100644 --- a/drivers/soc/dove/pmu.c +++ b/drivers/soc/dove/pmu.c @@ -222,9 +222,9 @@ static void __pmu_domain_register(struct pmu_domain *domain, } /* PMU IRQ controller */ -static void pmu_irq_handler(unsigned int irq, struct irq_desc *desc) +static void pmu_irq_handler(struct irq_desc *desc) { - struct pmu_data *pmu = irq_get_handler_data(irq); + struct pmu_data *pmu = irq_desc_get_handler_data(desc); struct irq_chip_generic *gc = pmu->irq_gc; struct irq_domain *domain = pmu->irq_domain; void __iomem *base = gc->reg_base; @@ -232,7 +232,7 @@ static void pmu_irq_handler(unsigned int irq, struct irq_desc *desc) u32 done = ~0; if (stat == 0) { - handle_bad_irq(irq, desc); + handle_bad_irq(desc); return; } diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index bf9ed380bb1c..63318e2afba1 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1720,6 +1720,7 @@ static int atmel_spi_runtime_resume(struct device *dev) return clk_prepare_enable(as->clk); } +#ifdef CONFIG_PM_SLEEP static int atmel_spi_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); @@ -1756,6 +1757,7 @@ static int atmel_spi_resume(struct device *dev) return ret; } +#endif static const struct dev_pm_ops atmel_spi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(atmel_spi_suspend, atmel_spi_resume) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index e7874a6171ec..3e8eeb23d4e9 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -386,14 +386,14 @@ static bool bcm2835_spi_can_dma(struct spi_master *master, /* otherwise we only allow transfers within the same page * to avoid wasting time on dma_mapping when it is not practical */ - if (((size_t)tfr->tx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) { + if (((size_t)tfr->tx_buf & (PAGE_SIZE - 1)) + tfr->len > PAGE_SIZE) { dev_warn_once(&spi->dev, "Unaligned spi tx-transfer bridging page\n"); return false; } - if (((size_t)tfr->rx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) { + if (((size_t)tfr->rx_buf & (PAGE_SIZE - 1)) + tfr->len > PAGE_SIZE) { dev_warn_once(&spi->dev, - "Unaligned spi tx-transfer bridging page\n"); + "Unaligned spi rx-transfer bridging page\n"); return false; } diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index 5468fc70dbf8..2465259f6241 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -444,6 +444,7 @@ static const struct of_device_id meson_spifc_dt_match[] = { { .compatible = "amlogic,meson6-spifc", }, { }, }; +MODULE_DEVICE_TABLE(of, meson_spifc_dt_match); static struct platform_driver meson_spifc_driver = { .probe = meson_spifc_probe, diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 5f6315c47920..ecb6c58238c4 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -85,7 +85,7 @@ struct mtk_spi { void __iomem *base; u32 state; u32 pad_sel; - struct clk *spi_clk, *parent_clk; + struct clk *parent_clk, *sel_clk, *spi_clk; struct spi_transfer *cur_transfer; u32 xfer_len; struct scatterlist *tx_sgl, *rx_sgl; @@ -173,22 +173,6 @@ static void mtk_spi_config(struct mtk_spi *mdata, writel(mdata->pad_sel, mdata->base + SPI_PAD_SEL_REG); } -static int mtk_spi_prepare_hardware(struct spi_master *master) -{ - struct spi_transfer *trans; - struct mtk_spi *mdata = spi_master_get_devdata(master); - struct spi_message *msg = master->cur_msg; - - trans = list_first_entry(&msg->transfers, struct spi_transfer, - transfer_list); - if (!trans->cs_change) { - mdata->state = MTK_SPI_IDLE; - mtk_spi_reset(mdata); - } - - return 0; -} - static int mtk_spi_prepare_message(struct spi_master *master, struct spi_message *msg) { @@ -228,11 +212,15 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable) struct mtk_spi *mdata = spi_master_get_devdata(spi->master); reg_val = readl(mdata->base + SPI_CMD_REG); - if (!enable) + if (!enable) { reg_val |= SPI_CMD_PAUSE_EN; - else + writel(reg_val, mdata->base + SPI_CMD_REG); + } else { reg_val &= ~SPI_CMD_PAUSE_EN; - writel(reg_val, mdata->base + SPI_CMD_REG); + writel(reg_val, mdata->base + SPI_CMD_REG); + mdata->state = MTK_SPI_IDLE; + mtk_spi_reset(mdata); + } } static void mtk_spi_prepare_transfer(struct spi_master *master, @@ -509,7 +497,6 @@ static int mtk_spi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA; master->set_cs = mtk_spi_set_cs; - master->prepare_transfer_hardware = mtk_spi_prepare_hardware; master->prepare_message = mtk_spi_prepare_message; master->transfer_one = mtk_spi_transfer_one; master->can_dma = mtk_spi_can_dma; @@ -576,13 +563,6 @@ static int mtk_spi_probe(struct platform_device *pdev) goto err_put_master; } - mdata->spi_clk = devm_clk_get(&pdev->dev, "spi-clk"); - if (IS_ERR(mdata->spi_clk)) { - ret = PTR_ERR(mdata->spi_clk); - dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret); - goto err_put_master; - } - mdata->parent_clk = devm_clk_get(&pdev->dev, "parent-clk"); if (IS_ERR(mdata->parent_clk)) { ret = PTR_ERR(mdata->parent_clk); @@ -590,13 +570,27 @@ static int mtk_spi_probe(struct platform_device *pdev) goto err_put_master; } + mdata->sel_clk = devm_clk_get(&pdev->dev, "sel-clk"); + if (IS_ERR(mdata->sel_clk)) { + ret = PTR_ERR(mdata->sel_clk); + dev_err(&pdev->dev, "failed to get sel-clk: %d\n", ret); + goto err_put_master; + } + + mdata->spi_clk = devm_clk_get(&pdev->dev, "spi-clk"); + if (IS_ERR(mdata->spi_clk)) { + ret = PTR_ERR(mdata->spi_clk); + dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret); + goto err_put_master; + } + ret = clk_prepare_enable(mdata->spi_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret); goto err_put_master; } - ret = clk_set_parent(mdata->spi_clk, mdata->parent_clk); + ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret); goto err_disable_clk; @@ -630,7 +624,6 @@ static int mtk_spi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); mtk_spi_reset(mdata); - clk_disable_unprepare(mdata->spi_clk); spi_master_put(master); return 0; diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index fdd791977041..a8ef38ebb9c9 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -654,6 +654,10 @@ static irqreturn_t ssp_int(int irq, void *dev_id) if (!(sccr1_reg & SSCR1_TIE)) mask &= ~SSSR_TFS; + /* Ignore RX timeout interrupt if it is disabled */ + if (!(sccr1_reg & SSCR1_TINTE)) + mask &= ~SSSR_TINT; + if (!(status & mask)) return IRQ_NONE; diff --git a/drivers/spi/spi-xtensa-xtfpga.c b/drivers/spi/spi-xtensa-xtfpga.c index 2e32ea2f194f..be6155cba9de 100644 --- a/drivers/spi/spi-xtensa-xtfpga.c +++ b/drivers/spi/spi-xtensa-xtfpga.c @@ -34,13 +34,13 @@ struct xtfpga_spi { static inline void xtfpga_spi_write32(const struct xtfpga_spi *spi, unsigned addr, u32 val) { - iowrite32(val, spi->regs + addr); + __raw_writel(val, spi->regs + addr); } static inline unsigned int xtfpga_spi_read32(const struct xtfpga_spi *spi, unsigned addr) { - return ioread32(spi->regs + addr); + return __raw_readl(spi->regs + addr); } static inline void xtfpga_spi_wait_busy(struct xtfpga_spi *xspi) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 3abb3903f2ad..a5f53de813d3 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1610,8 +1610,7 @@ static struct class spi_master_class = { * * The caller is responsible for assigning the bus number and initializing * the master's methods before calling spi_register_master(); and (after errors - * adding the device) calling spi_master_put() and kfree() to prevent a memory - * leak. + * adding the device) calling spi_master_put() to prevent a memory leak. */ struct spi_master *spi_alloc_master(struct device *dev, unsigned size) { diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index fba92a526531..ef008e52f953 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -651,7 +651,8 @@ static int spidev_release(struct inode *inode, struct file *filp) kfree(spidev->rx_buffer); spidev->rx_buffer = NULL; - spidev->speed_hz = spidev->spi->max_speed_hz; + if (spidev->spi) + spidev->speed_hz = spidev->spi->max_speed_hz; /* ... after we unbound from the underlying device? */ spin_lock_irq(&spidev->spi_lock); diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index bdfb3c84c3cb..4a3cf9ba152f 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -451,7 +451,7 @@ static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid) } } -static void pmic_arb_chained_irq(unsigned int irq, struct irq_desc *desc) +static void pmic_arb_chained_irq(struct irq_desc *desc) { struct spmi_pmic_arb_dev *pa = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index a48a7439a206..8cf23a2468ba 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -876,7 +876,6 @@ int ssb_bus_pcibus_register(struct ssb_bus *bus, struct pci_dev *host_pci) return err; } -EXPORT_SYMBOL(ssb_bus_pcibus_register); #endif /* CONFIG_SSB_PCIHOST */ #ifdef CONFIG_SSB_PCMCIAHOST diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c index b413e0187087..f03422bbf087 100644 --- a/drivers/ssb/pcmcia.c +++ b/drivers/ssb/pcmcia.c @@ -147,8 +147,7 @@ error: return err; } -int ssb_pcmcia_switch_core(struct ssb_bus *bus, - struct ssb_device *dev) +static int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev) { int err; diff --git a/drivers/ssb/sdio.c b/drivers/ssb/sdio.c index b2d36f7736c5..2278e43614bd 100644 --- a/drivers/ssb/sdio.c +++ b/drivers/ssb/sdio.c @@ -200,7 +200,7 @@ out: } /* host must be already claimed */ -int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev) +static int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev) { u8 coreidx = dev->core_index; u32 sbaddr; diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index eb507a50a564..8a2ebc88c66f 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -85,8 +85,6 @@ static inline int ssb_pci_init(struct ssb_bus *bus) /* pcmcia.c */ #ifdef CONFIG_SSB_PCMCIAHOST -extern int ssb_pcmcia_switch_core(struct ssb_bus *bus, - struct ssb_device *dev); extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, u8 coreidx); extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus, @@ -98,11 +96,6 @@ extern void ssb_pcmcia_exit(struct ssb_bus *bus); extern int ssb_pcmcia_init(struct ssb_bus *bus); extern const struct ssb_bus_ops ssb_pcmcia_ops; #else /* CONFIG_SSB_PCMCIAHOST */ -static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus, - struct ssb_device *dev) -{ - return 0; -} static inline int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { @@ -132,9 +125,7 @@ extern int ssb_sdio_get_invariants(struct ssb_bus *bus, struct ssb_init_invariants *iv); extern u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset); -extern int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev); extern int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx); -extern int ssb_sdio_hardware_setup(struct ssb_bus *bus); extern void ssb_sdio_exit(struct ssb_bus *bus); extern int ssb_sdio_init(struct ssb_bus *bus); @@ -144,19 +135,10 @@ static inline u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset) { return 0; } -static inline int ssb_sdio_switch_core(struct ssb_bus *bus, - struct ssb_device *dev) -{ - return 0; -} static inline int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { return 0; } -static inline int ssb_sdio_hardware_setup(struct ssb_bus *bus) -{ - return 0; -} static inline void ssb_sdio_exit(struct ssb_bus *bus) { } diff --git a/drivers/staging/android/TODO b/drivers/staging/android/TODO index 20288fc53946..8f3ac37bfe12 100644 --- a/drivers/staging/android/TODO +++ b/drivers/staging/android/TODO @@ -5,5 +5,25 @@ TODO: - add proper arch dependencies as needed - audit userspace interfaces to make sure they are sane + +ion/ + - Remove ION_IOC_SYNC: Flushing for devices should be purely a kernel internal + interface on top of dma-buf. flush_for_device needs to be added to dma-buf + first. + - Remove ION_IOC_CUSTOM: Atm used for cache flushing for cpu access in some + vendor trees. Should be replaced with an ioctl on the dma-buf to expose the + begin/end_cpu_access hooks to userspace. + - Clarify the tricks ion plays with explicitly managing coherency behind the + dma api's back (this is absolutely needed for high-perf gpu drivers): Add an + explicit coherency management mode to flush_for_device to be used by drivers + which want to manage caches themselves and which indicates whether cpu caches + need flushing. + - With those removed there's probably no use for ION_IOC_IMPORT anymore either + since ion would just be the central allocator for shared buffers. + - Add dt-binding to expose cma regions as ion heaps, with the rule that any + such cma regions must already be used by some device for dma. I.e. ion only + exposes existing cma regions and doesn't reserve unecessarily memory when + booting a system which doesn't use ion. + Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc: Arve HjønnevÃ¥g <arve@android.com> and Riley Andrews <riandrews@android.com> diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 217aa537c4eb..6e8d8392ca38 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -1179,13 +1179,13 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) mutex_unlock(&client->lock); goto end; } - mutex_unlock(&client->lock); handle = ion_handle_create(client, buffer); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { + mutex_unlock(&client->lock); goto end; + } - mutex_lock(&client->lock); ret = ion_handle_add(client, handle); mutex_unlock(&client->lock); if (ret) { diff --git a/drivers/staging/board/armadillo800eva.c b/drivers/staging/board/armadillo800eva.c index 81df77bd55cc..9c41652ee908 100644 --- a/drivers/staging/board/armadillo800eva.c +++ b/drivers/staging/board/armadillo800eva.c @@ -91,7 +91,7 @@ static const struct board_staging_dev armadillo800eva_devices[] __initconst = { .pdev = &lcdc0_device, .clocks = lcdc0_clocks, .nclocks = ARRAY_SIZE(lcdc0_clocks), - .domain = "a4lc", + .domain = "/system-controller@e6180000/pm-domains/c5/a4lc@1" }, }; diff --git a/drivers/staging/board/board.c b/drivers/staging/board/board.c index 29d456e29f38..3eb5eb8f069c 100644 --- a/drivers/staging/board/board.c +++ b/drivers/staging/board/board.c @@ -135,6 +135,40 @@ int __init board_staging_register_clock(const struct board_staging_clk *bsc) return error; } +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF +static int board_staging_add_dev_domain(struct platform_device *pdev, + const char *domain) +{ + struct of_phandle_args pd_args; + struct generic_pm_domain *pd; + struct device_node *np; + + np = of_find_node_by_path(domain); + if (!np) { + pr_err("Cannot find domain node %s\n", domain); + return -ENOENT; + } + + pd_args.np = np; + pd_args.args_count = 0; + pd = of_genpd_get_from_provider(&pd_args); + if (IS_ERR(pd)) { + pr_err("Cannot find genpd %s (%ld)\n", domain, PTR_ERR(pd)); + return PTR_ERR(pd); + + } + pr_debug("Found genpd %s for device %s\n", pd->name, pdev->name); + + return pm_genpd_add_device(pd, &pdev->dev); +} +#else +static inline int board_staging_add_dev_domain(struct platform_device *pdev, + const char *domain) +{ + return 0; +} +#endif + int __init board_staging_register_device(const struct board_staging_dev *dev) { struct platform_device *pdev = dev->pdev; @@ -161,7 +195,7 @@ int __init board_staging_register_device(const struct board_staging_dev *dev) } if (dev->domain) - __pm_genpd_name_add_device(dev->domain, &pdev->dev, NULL); + board_staging_add_dev_domain(pdev, dev->domain); return error; } diff --git a/drivers/staging/fbtft/fb_uc1611.c b/drivers/staging/fbtft/fb_uc1611.c index 32f3a9d921d6..5cafa50d1fac 100644 --- a/drivers/staging/fbtft/fb_uc1611.c +++ b/drivers/staging/fbtft/fb_uc1611.c @@ -76,7 +76,7 @@ static int init_display(struct fbtft_par *par) /* Set CS active high */ par->spi->mode |= SPI_CS_HIGH; - ret = par->spi->master->setup(par->spi); + ret = spi_setup(par->spi); if (ret) { dev_err(par->info->device, "Could not set SPI_CS_HIGH\n"); return ret; diff --git a/drivers/staging/fbtft/fb_watterott.c b/drivers/staging/fbtft/fb_watterott.c index 88fb2c0132d5..8eae6ef25846 100644 --- a/drivers/staging/fbtft/fb_watterott.c +++ b/drivers/staging/fbtft/fb_watterott.c @@ -169,7 +169,7 @@ static int init_display(struct fbtft_par *par) /* enable SPI interface by having CS and MOSI low during reset */ save_mode = par->spi->mode; par->spi->mode |= SPI_CS_HIGH; - ret = par->spi->master->setup(par->spi); /* set CS inactive low */ + ret = spi_setup(par->spi); /* set CS inactive low */ if (ret) { dev_err(par->info->device, "Could not set SPI_CS_HIGH\n"); return ret; @@ -180,7 +180,7 @@ static int init_display(struct fbtft_par *par) par->fbtftops.reset(par); mdelay(1000); par->spi->mode = save_mode; - ret = par->spi->master->setup(par->spi); + ret = spi_setup(par->spi); if (ret) { dev_err(par->info->device, "Could not restore SPI mode\n"); return ret; diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index 23392eb6799e..7f5fa3d1cab0 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -1436,15 +1436,11 @@ int fbtft_probe_common(struct fbtft_display *display, /* 9-bit SPI setup */ if (par->spi && display->buswidth == 9) { - par->spi->bits_per_word = 9; - ret = par->spi->master->setup(par->spi); - if (ret) { + if (par->spi->master->bits_per_word_mask & SPI_BPW_MASK(9)) { + par->spi->bits_per_word = 9; + } else { dev_warn(&par->spi->dev, "9-bit SPI not available, emulating using 8-bit.\n"); - par->spi->bits_per_word = 8; - ret = par->spi->master->setup(par->spi); - if (ret) - goto out_release; /* allocate buffer with room for dc bits */ par->extra = devm_kzalloc(par->info->device, par->txbuf.len + (par->txbuf.len / 8) + 8, diff --git a/drivers/staging/fbtft/flexfb.c b/drivers/staging/fbtft/flexfb.c index c763efc5de7d..3f380a0086c3 100644 --- a/drivers/staging/fbtft/flexfb.c +++ b/drivers/staging/fbtft/flexfb.c @@ -463,15 +463,12 @@ static int flexfb_probe_common(struct spi_device *sdev, } par->fbtftops.write_register = fbtft_write_reg8_bus9; par->fbtftops.write_vmem = fbtft_write_vmem16_bus9; - sdev->bits_per_word = 9; - ret = sdev->master->setup(sdev); - if (ret) { + if (par->spi->master->bits_per_word_mask + & SPI_BPW_MASK(9)) { + par->spi->bits_per_word = 9; + } else { dev_warn(dev, "9-bit SPI not available, emulating using 8-bit.\n"); - sdev->bits_per_word = 8; - ret = sdev->master->setup(sdev); - if (ret) - goto out_release; /* allocate buffer with room for dc bits */ par->extra = devm_kzalloc(par->info->device, par->txbuf.len + (par->txbuf.len / 8) + 8, diff --git a/drivers/staging/lustre/README.txt b/drivers/staging/lustre/README.txt index cf0ca50ff83b..0676243eea9e 100644 --- a/drivers/staging/lustre/README.txt +++ b/drivers/staging/lustre/README.txt @@ -14,10 +14,8 @@ Unlike shared disk storage cluster filesystems (e.g. OCFS2, GFS, GPFS), Lustre has independent Metadata and Data servers that clients can access in parallel to maximize performance. -In order to use Lustre client you will need to download lustre client -tools from -https://downloads.hpdd.intel.com/public/lustre/latest-feature-release/ -the package name is lustre-client. +In order to use Lustre client you will need to download the "lustre-client" +package that contains the userspace tools from http://lustre.org/download/ You will need to install and configure your Lustre servers separately. @@ -76,12 +74,10 @@ Mount Options More Information ================ -You can get more information at -OpenSFS website: http://lustre.opensfs.org/about/ -Intel HPDD wiki: https://wiki.hpdd.intel.com +You can get more information at the Lustre website: http://wiki.lustre.org/ -Out of tree Lustre client and server code is available at: -http://git.whamcloud.com/fs/lustre-release.git +Source for the userspace tools and out-of-tree client and server code +is available at: http://git.hpdd.intel.com/fs/lustre-release.git Latest binary packages: -http://lustre.opensfs.org/download-lustre/ +http://lustre.org/download/ diff --git a/drivers/staging/most/Kconfig b/drivers/staging/most/Kconfig index d50de03de7b9..0b9b9b539f70 100644 --- a/drivers/staging/most/Kconfig +++ b/drivers/staging/most/Kconfig @@ -1,5 +1,6 @@ menuconfig MOST tristate "MOST driver" + depends on HAS_DMA select MOSTCORE default n ---help--- diff --git a/drivers/staging/most/hdm-dim2/Kconfig b/drivers/staging/most/hdm-dim2/Kconfig index 1d4ad1d67758..fc548769479b 100644 --- a/drivers/staging/most/hdm-dim2/Kconfig +++ b/drivers/staging/most/hdm-dim2/Kconfig @@ -5,6 +5,7 @@ config HDM_DIM2 tristate "DIM2 HDM" depends on AIM_NETWORK + depends on HAS_IOMEM ---help--- Say Y here if you want to connect via MediaLB to network transceiver. diff --git a/drivers/staging/most/hdm-usb/Kconfig b/drivers/staging/most/hdm-usb/Kconfig index a482c3fdf34b..ec1546312ee6 100644 --- a/drivers/staging/most/hdm-usb/Kconfig +++ b/drivers/staging/most/hdm-usb/Kconfig @@ -4,7 +4,7 @@ config HDM_USB tristate "USB HDM" - depends on USB + depends on USB && NET select AIM_NETWORK ---help--- Say Y here if you want to connect via USB to network tranceiver. diff --git a/drivers/staging/most/mostcore/Kconfig b/drivers/staging/most/mostcore/Kconfig index 38abf1b21b66..47172546d728 100644 --- a/drivers/staging/most/mostcore/Kconfig +++ b/drivers/staging/most/mostcore/Kconfig @@ -4,6 +4,7 @@ config MOSTCORE tristate "MOST Core" + depends on HAS_DMA ---help--- Say Y here if you want to enable MOST support. diff --git a/drivers/staging/rdma/Kconfig b/drivers/staging/rdma/Kconfig index cf5fe9bb87a1..d7f62359d743 100644 --- a/drivers/staging/rdma/Kconfig +++ b/drivers/staging/rdma/Kconfig @@ -24,6 +24,8 @@ if STAGING_RDMA source "drivers/staging/rdma/amso1100/Kconfig" +source "drivers/staging/rdma/ehca/Kconfig" + source "drivers/staging/rdma/hfi1/Kconfig" source "drivers/staging/rdma/ipath/Kconfig" diff --git a/drivers/staging/rdma/Makefile b/drivers/staging/rdma/Makefile index cbd915ac7f20..139d78ef2c24 100644 --- a/drivers/staging/rdma/Makefile +++ b/drivers/staging/rdma/Makefile @@ -1,4 +1,5 @@ # Entries for RDMA_STAGING tree obj-$(CONFIG_INFINIBAND_AMSO1100) += amso1100/ +obj-$(CONFIG_INFINIBAND_EHCA) += ehca/ obj-$(CONFIG_INFINIBAND_HFI1) += hfi1/ obj-$(CONFIG_INFINIBAND_IPATH) += ipath/ diff --git a/drivers/infiniband/hw/ehca/Kconfig b/drivers/staging/rdma/ehca/Kconfig index 59f807d8d58e..3fadd2ad6426 100644 --- a/drivers/infiniband/hw/ehca/Kconfig +++ b/drivers/staging/rdma/ehca/Kconfig @@ -2,7 +2,8 @@ config INFINIBAND_EHCA tristate "eHCA support" depends on IBMEBUS ---help--- - This driver supports the IBM pSeries eHCA InfiniBand adapter. + This driver supports the deprecated IBM pSeries eHCA InfiniBand + adapter. To compile the driver as a module, choose M here. The module will be called ib_ehca. diff --git a/drivers/infiniband/hw/ehca/Makefile b/drivers/staging/rdma/ehca/Makefile index 74d284e46a40..74d284e46a40 100644 --- a/drivers/infiniband/hw/ehca/Makefile +++ b/drivers/staging/rdma/ehca/Makefile diff --git a/drivers/staging/rdma/ehca/TODO b/drivers/staging/rdma/ehca/TODO new file mode 100644 index 000000000000..199a4a600142 --- /dev/null +++ b/drivers/staging/rdma/ehca/TODO @@ -0,0 +1,4 @@ +9/2015 + +The ehca driver has been deprecated and moved to drivers/staging/rdma. +It will be removed in the 4.6 merge window. diff --git a/drivers/infiniband/hw/ehca/ehca_av.c b/drivers/staging/rdma/ehca/ehca_av.c index 465926319f3d..465926319f3d 100644 --- a/drivers/infiniband/hw/ehca/ehca_av.c +++ b/drivers/staging/rdma/ehca/ehca_av.c diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/staging/rdma/ehca/ehca_classes.h index bd45e0f3923f..bd45e0f3923f 100644 --- a/drivers/infiniband/hw/ehca/ehca_classes.h +++ b/drivers/staging/rdma/ehca/ehca_classes.h diff --git a/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h b/drivers/staging/rdma/ehca/ehca_classes_pSeries.h index 689c35786dd2..689c35786dd2 100644 --- a/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h +++ b/drivers/staging/rdma/ehca/ehca_classes_pSeries.h diff --git a/drivers/infiniband/hw/ehca/ehca_cq.c b/drivers/staging/rdma/ehca/ehca_cq.c index 9b68b175069b..9b68b175069b 100644 --- a/drivers/infiniband/hw/ehca/ehca_cq.c +++ b/drivers/staging/rdma/ehca/ehca_cq.c diff --git a/drivers/infiniband/hw/ehca/ehca_eq.c b/drivers/staging/rdma/ehca/ehca_eq.c index 90da6747d395..90da6747d395 100644 --- a/drivers/infiniband/hw/ehca/ehca_eq.c +++ b/drivers/staging/rdma/ehca/ehca_eq.c diff --git a/drivers/infiniband/hw/ehca/ehca_hca.c b/drivers/staging/rdma/ehca/ehca_hca.c index e8b1bb65797a..e8b1bb65797a 100644 --- a/drivers/infiniband/hw/ehca/ehca_hca.c +++ b/drivers/staging/rdma/ehca/ehca_hca.c diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/staging/rdma/ehca/ehca_irq.c index 8615d7cf7e01..8615d7cf7e01 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.c +++ b/drivers/staging/rdma/ehca/ehca_irq.c diff --git a/drivers/infiniband/hw/ehca/ehca_irq.h b/drivers/staging/rdma/ehca/ehca_irq.h index 5370199f08c7..5370199f08c7 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.h +++ b/drivers/staging/rdma/ehca/ehca_irq.h diff --git a/drivers/infiniband/hw/ehca/ehca_iverbs.h b/drivers/staging/rdma/ehca/ehca_iverbs.h index 80e6a3d5df3e..80e6a3d5df3e 100644 --- a/drivers/infiniband/hw/ehca/ehca_iverbs.h +++ b/drivers/staging/rdma/ehca/ehca_iverbs.h diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/staging/rdma/ehca/ehca_main.c index 8246418cd4e0..8246418cd4e0 100644 --- a/drivers/infiniband/hw/ehca/ehca_main.c +++ b/drivers/staging/rdma/ehca/ehca_main.c diff --git a/drivers/infiniband/hw/ehca/ehca_mcast.c b/drivers/staging/rdma/ehca/ehca_mcast.c index cec181532924..cec181532924 100644 --- a/drivers/infiniband/hw/ehca/ehca_mcast.c +++ b/drivers/staging/rdma/ehca/ehca_mcast.c diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.c b/drivers/staging/rdma/ehca/ehca_mrmw.c index f914b30999f8..f914b30999f8 100644 --- a/drivers/infiniband/hw/ehca/ehca_mrmw.c +++ b/drivers/staging/rdma/ehca/ehca_mrmw.c diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.h b/drivers/staging/rdma/ehca/ehca_mrmw.h index 50d8b51306dd..50d8b51306dd 100644 --- a/drivers/infiniband/hw/ehca/ehca_mrmw.h +++ b/drivers/staging/rdma/ehca/ehca_mrmw.h diff --git a/drivers/infiniband/hw/ehca/ehca_pd.c b/drivers/staging/rdma/ehca/ehca_pd.c index 351577a6670a..351577a6670a 100644 --- a/drivers/infiniband/hw/ehca/ehca_pd.c +++ b/drivers/staging/rdma/ehca/ehca_pd.c diff --git a/drivers/infiniband/hw/ehca/ehca_qes.h b/drivers/staging/rdma/ehca/ehca_qes.h index 90c4efa67586..90c4efa67586 100644 --- a/drivers/infiniband/hw/ehca/ehca_qes.h +++ b/drivers/staging/rdma/ehca/ehca_qes.h diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/staging/rdma/ehca/ehca_qp.c index 2e89356c46fa..2e89356c46fa 100644 --- a/drivers/infiniband/hw/ehca/ehca_qp.c +++ b/drivers/staging/rdma/ehca/ehca_qp.c diff --git a/drivers/infiniband/hw/ehca/ehca_reqs.c b/drivers/staging/rdma/ehca/ehca_reqs.c index 47f94984353d..47f94984353d 100644 --- a/drivers/infiniband/hw/ehca/ehca_reqs.c +++ b/drivers/staging/rdma/ehca/ehca_reqs.c diff --git a/drivers/infiniband/hw/ehca/ehca_sqp.c b/drivers/staging/rdma/ehca/ehca_sqp.c index 376b031c2c7f..376b031c2c7f 100644 --- a/drivers/infiniband/hw/ehca/ehca_sqp.c +++ b/drivers/staging/rdma/ehca/ehca_sqp.c diff --git a/drivers/infiniband/hw/ehca/ehca_tools.h b/drivers/staging/rdma/ehca/ehca_tools.h index d280b12aae64..d280b12aae64 100644 --- a/drivers/infiniband/hw/ehca/ehca_tools.h +++ b/drivers/staging/rdma/ehca/ehca_tools.h diff --git a/drivers/infiniband/hw/ehca/ehca_uverbs.c b/drivers/staging/rdma/ehca/ehca_uverbs.c index 1a1d5d99fcf9..1a1d5d99fcf9 100644 --- a/drivers/infiniband/hw/ehca/ehca_uverbs.c +++ b/drivers/staging/rdma/ehca/ehca_uverbs.c diff --git a/drivers/infiniband/hw/ehca/hcp_if.c b/drivers/staging/rdma/ehca/hcp_if.c index 89517ffb4389..89517ffb4389 100644 --- a/drivers/infiniband/hw/ehca/hcp_if.c +++ b/drivers/staging/rdma/ehca/hcp_if.c diff --git a/drivers/infiniband/hw/ehca/hcp_if.h b/drivers/staging/rdma/ehca/hcp_if.h index a46e514c367b..a46e514c367b 100644 --- a/drivers/infiniband/hw/ehca/hcp_if.h +++ b/drivers/staging/rdma/ehca/hcp_if.h diff --git a/drivers/infiniband/hw/ehca/hcp_phyp.c b/drivers/staging/rdma/ehca/hcp_phyp.c index 077376ff3d28..077376ff3d28 100644 --- a/drivers/infiniband/hw/ehca/hcp_phyp.c +++ b/drivers/staging/rdma/ehca/hcp_phyp.c diff --git a/drivers/infiniband/hw/ehca/hcp_phyp.h b/drivers/staging/rdma/ehca/hcp_phyp.h index d1b029910249..d1b029910249 100644 --- a/drivers/infiniband/hw/ehca/hcp_phyp.h +++ b/drivers/staging/rdma/ehca/hcp_phyp.h diff --git a/drivers/infiniband/hw/ehca/hipz_fns.h b/drivers/staging/rdma/ehca/hipz_fns.h index 9dac93d02140..9dac93d02140 100644 --- a/drivers/infiniband/hw/ehca/hipz_fns.h +++ b/drivers/staging/rdma/ehca/hipz_fns.h diff --git a/drivers/infiniband/hw/ehca/hipz_fns_core.h b/drivers/staging/rdma/ehca/hipz_fns_core.h index 868735fd3187..868735fd3187 100644 --- a/drivers/infiniband/hw/ehca/hipz_fns_core.h +++ b/drivers/staging/rdma/ehca/hipz_fns_core.h diff --git a/drivers/infiniband/hw/ehca/hipz_hw.h b/drivers/staging/rdma/ehca/hipz_hw.h index bf996c7acc42..bf996c7acc42 100644 --- a/drivers/infiniband/hw/ehca/hipz_hw.h +++ b/drivers/staging/rdma/ehca/hipz_hw.h diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.c b/drivers/staging/rdma/ehca/ipz_pt_fn.c index 7ffc748cb973..7ffc748cb973 100644 --- a/drivers/infiniband/hw/ehca/ipz_pt_fn.c +++ b/drivers/staging/rdma/ehca/ipz_pt_fn.c diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.h b/drivers/staging/rdma/ehca/ipz_pt_fn.h index a801274ea337..a801274ea337 100644 --- a/drivers/infiniband/hw/ehca/ipz_pt_fn.h +++ b/drivers/staging/rdma/ehca/ipz_pt_fn.h diff --git a/drivers/staging/rdma/hfi1/chip.c b/drivers/staging/rdma/hfi1/chip.c index 654eafef1d30..aa58e597df06 100644 --- a/drivers/staging/rdma/hfi1/chip.c +++ b/drivers/staging/rdma/hfi1/chip.c @@ -2710,7 +2710,7 @@ int acquire_lcb_access(struct hfi1_devdata *dd, int sleep_ok) if (sleep_ok) { mutex_lock(&ppd->hls_lock); } else { - while (mutex_trylock(&ppd->hls_lock) == EBUSY) + while (!mutex_trylock(&ppd->hls_lock)) udelay(1); } @@ -2758,7 +2758,7 @@ int release_lcb_access(struct hfi1_devdata *dd, int sleep_ok) if (sleep_ok) { mutex_lock(&dd->pport->hls_lock); } else { - while (mutex_trylock(&dd->pport->hls_lock) == EBUSY) + while (!mutex_trylock(&dd->pport->hls_lock)) udelay(1); } diff --git a/drivers/staging/rdma/hfi1/device.c b/drivers/staging/rdma/hfi1/device.c index 07c87a87775f..bc26a5392712 100644 --- a/drivers/staging/rdma/hfi1/device.c +++ b/drivers/staging/rdma/hfi1/device.c @@ -57,11 +57,13 @@ #include "device.h" static struct class *class; +static struct class *user_class; static dev_t hfi1_dev; int hfi1_cdev_init(int minor, const char *name, const struct file_operations *fops, - struct cdev *cdev, struct device **devp) + struct cdev *cdev, struct device **devp, + bool user_accessible) { const dev_t dev = MKDEV(MAJOR(hfi1_dev), minor); struct device *device = NULL; @@ -78,7 +80,11 @@ int hfi1_cdev_init(int minor, const char *name, goto done; } - device = device_create(class, NULL, dev, NULL, "%s", name); + if (user_accessible) + device = device_create(user_class, NULL, dev, NULL, "%s", name); + else + device = device_create(class, NULL, dev, NULL, "%s", name); + if (!IS_ERR(device)) goto done; ret = PTR_ERR(device); @@ -110,6 +116,26 @@ const char *class_name(void) return hfi1_class_name; } +static char *hfi1_devnode(struct device *dev, umode_t *mode) +{ + if (mode) + *mode = 0600; + return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); +} + +static const char *hfi1_class_name_user = "hfi1_user"; +const char *class_name_user(void) +{ + return hfi1_class_name_user; +} + +static char *hfi1_user_devnode(struct device *dev, umode_t *mode) +{ + if (mode) + *mode = 0666; + return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); +} + int __init dev_init(void) { int ret; @@ -125,7 +151,22 @@ int __init dev_init(void) ret = PTR_ERR(class); pr_err("Could not create device class (err %d)\n", -ret); unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); + goto done; } + class->devnode = hfi1_devnode; + + user_class = class_create(THIS_MODULE, class_name_user()); + if (IS_ERR(user_class)) { + ret = PTR_ERR(user_class); + pr_err("Could not create device class for user accessible files (err %d)\n", + -ret); + class_destroy(class); + class = NULL; + user_class = NULL; + unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); + goto done; + } + user_class->devnode = hfi1_user_devnode; done: return ret; @@ -133,10 +174,11 @@ done: void dev_cleanup(void) { - if (class) { - class_destroy(class); - class = NULL; - } + class_destroy(class); + class = NULL; + + class_destroy(user_class); + user_class = NULL; unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); } diff --git a/drivers/staging/rdma/hfi1/device.h b/drivers/staging/rdma/hfi1/device.h index 98caecd3d807..2850ff739d81 100644 --- a/drivers/staging/rdma/hfi1/device.h +++ b/drivers/staging/rdma/hfi1/device.h @@ -52,7 +52,8 @@ int hfi1_cdev_init(int minor, const char *name, const struct file_operations *fops, - struct cdev *cdev, struct device **devp); + struct cdev *cdev, struct device **devp, + bool user_accessible); void hfi1_cdev_cleanup(struct cdev *cdev, struct device **devp); const char *class_name(void); int __init dev_init(void); diff --git a/drivers/staging/rdma/hfi1/diag.c b/drivers/staging/rdma/hfi1/diag.c index 6777d6b659cf..3e8d5ac4c626 100644 --- a/drivers/staging/rdma/hfi1/diag.c +++ b/drivers/staging/rdma/hfi1/diag.c @@ -292,7 +292,7 @@ int hfi1_diag_add(struct hfi1_devdata *dd) if (atomic_inc_return(&diagpkt_count) == 1) { ret = hfi1_cdev_init(HFI1_DIAGPKT_MINOR, name, &diagpkt_file_ops, &diagpkt_cdev, - &diagpkt_device); + &diagpkt_device, false); } return ret; @@ -592,7 +592,8 @@ static int hfi1_snoop_add(struct hfi1_devdata *dd, const char *name) ret = hfi1_cdev_init(HFI1_SNOOP_CAPTURE_BASE + dd->unit, name, &snoop_file_ops, - &dd->hfi1_snoop.cdev, &dd->hfi1_snoop.class_dev); + &dd->hfi1_snoop.cdev, &dd->hfi1_snoop.class_dev, + false); if (ret) { dd_dev_err(dd, "Couldn't create %s device: %d", name, ret); @@ -1012,11 +1013,10 @@ static long hfi1_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) case HFI1_SNOOP_IOCSETLINKSTATE_EXTRA: memset(&link_info, 0, sizeof(link_info)); - ret = copy_from_user(&link_info, + if (copy_from_user(&link_info, (struct hfi1_link_info __user *)arg, - sizeof(link_info)); - if (ret) - break; + sizeof(link_info))) + ret = -EFAULT; value = link_info.port_state; index = link_info.port_number; @@ -1080,9 +1080,10 @@ static long hfi1_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) case HFI1_SNOOP_IOCGETLINKSTATE_EXTRA: if (cmd == HFI1_SNOOP_IOCGETLINKSTATE_EXTRA) { memset(&link_info, 0, sizeof(link_info)); - ret = copy_from_user(&link_info, + if (copy_from_user(&link_info, (struct hfi1_link_info __user *)arg, - sizeof(link_info)); + sizeof(link_info))) + ret = -EFAULT; index = link_info.port_number; } else { ret = __get_user(index, (int __user *) arg); @@ -1114,9 +1115,10 @@ static long hfi1_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ppd->link_speed_active; link_info.link_width_active = ppd->link_width_active; - ret = copy_to_user( + if (copy_to_user( (struct hfi1_link_info __user *)arg, - &link_info, sizeof(link_info)); + &link_info, sizeof(link_info))) + ret = -EFAULT; } else { ret = __put_user(value, (int __user *)arg); } @@ -1142,10 +1144,9 @@ static long hfi1_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) snoop_dbg("Setting filter"); /* just copy command structure */ argp = (unsigned long *)arg; - ret = copy_from_user(&filter_cmd, (void __user *)argp, - sizeof(filter_cmd)); - if (ret < 0) { - pr_alert("Error copying filter command\n"); + if (copy_from_user(&filter_cmd, (void __user *)argp, + sizeof(filter_cmd))) { + ret = -EFAULT; break; } if (filter_cmd.opcode >= HFI1_MAX_FILTERS) { @@ -1167,12 +1168,11 @@ static long hfi1_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) break; } /* copy remaining data from userspace */ - ret = copy_from_user((u8 *)filter_value, + if (copy_from_user((u8 *)filter_value, (void __user *)filter_cmd.value_ptr, - filter_cmd.length); - if (ret < 0) { + filter_cmd.length)) { kfree(filter_value); - pr_alert("Error copying filter data\n"); + ret = -EFAULT; break; } /* Drain packets first */ diff --git a/drivers/staging/rdma/hfi1/file_ops.c b/drivers/staging/rdma/hfi1/file_ops.c index 469861750b76..72d38500d8ce 100644 --- a/drivers/staging/rdma/hfi1/file_ops.c +++ b/drivers/staging/rdma/hfi1/file_ops.c @@ -1181,6 +1181,7 @@ static int get_ctxt_info(struct file *fp, void __user *ubase, __u32 len) struct hfi1_filedata *fd = fp->private_data; int ret = 0; + memset(&cinfo, 0, sizeof(cinfo)); ret = hfi1_get_base_kinfo(uctxt, &cinfo); if (ret < 0) goto done; @@ -2089,14 +2090,16 @@ static int user_add(struct hfi1_devdata *dd) if (atomic_inc_return(&user_count) == 1) { ret = hfi1_cdev_init(0, class_name(), &hfi1_file_ops, - &wildcard_cdev, &wildcard_device); + &wildcard_cdev, &wildcard_device, + true); if (ret) goto done; } snprintf(name, sizeof(name), "%s_%d", class_name(), dd->unit); ret = hfi1_cdev_init(dd->unit + 1, name, &hfi1_file_ops, - &dd->user_cdev, &dd->user_device); + &dd->user_cdev, &dd->user_device, + true); if (ret) goto done; @@ -2104,7 +2107,8 @@ static int user_add(struct hfi1_devdata *dd) snprintf(name, sizeof(name), "%s_ui%d", class_name(), dd->unit); ret = hfi1_cdev_init(dd->unit + UI_OFFSET, name, &ui_file_ops, - &dd->ui_cdev, &dd->ui_device); + &dd->ui_cdev, &dd->ui_device, + false); if (ret) goto done; } diff --git a/drivers/staging/rdma/hfi1/mad.c b/drivers/staging/rdma/hfi1/mad.c index 37269eb90c34..b2c1b72d38ce 100644 --- a/drivers/staging/rdma/hfi1/mad.c +++ b/drivers/staging/rdma/hfi1/mad.c @@ -1717,9 +1717,9 @@ static int __subn_get_opa_psi(struct opa_smp *smp, u32 am, u8 *data, psi->port_states.portphysstate_portstate = (hfi1_ibphys_portstate(ppd) << 4) | (lstate & 0xf); psi->link_width_downgrade_tx_active = - ppd->link_width_downgrade_tx_active; + cpu_to_be16(ppd->link_width_downgrade_tx_active); psi->link_width_downgrade_rx_active = - ppd->link_width_downgrade_rx_active; + cpu_to_be16(ppd->link_width_downgrade_rx_active); if (resp_len) *resp_len += sizeof(struct opa_port_state_info); diff --git a/drivers/staging/rdma/hfi1/sdma.c b/drivers/staging/rdma/hfi1/sdma.c index a8c903caecce..aecd1a74741c 100644 --- a/drivers/staging/rdma/hfi1/sdma.c +++ b/drivers/staging/rdma/hfi1/sdma.c @@ -737,7 +737,7 @@ u16 sdma_get_descq_cnt(void) */ if (!is_power_of_2(count)) return SDMA_DESCQ_CNT; - if (count < 64 && count > 32768) + if (count < 64 || count > 32768) return SDMA_DESCQ_CNT; return count; } @@ -1848,7 +1848,7 @@ static void dump_sdma_state(struct sdma_engine *sde) dd_dev_err(sde->dd, "\taidx: %u amode: %u alen: %u\n", (u8)((desc[1] & SDMA_DESC1_HEADER_INDEX_SMASK) - >> SDMA_DESC1_HEADER_INDEX_MASK), + >> SDMA_DESC1_HEADER_INDEX_SHIFT), (u8)((desc[1] & SDMA_DESC1_HEADER_MODE_SMASK) >> SDMA_DESC1_HEADER_MODE_SHIFT), (u8)((desc[1] & SDMA_DESC1_HEADER_DWS_SMASK) @@ -1926,7 +1926,7 @@ void sdma_seqfile_dump_sde(struct seq_file *s, struct sdma_engine *sde) if (desc[0] & SDMA_DESC0_FIRST_DESC_FLAG) seq_printf(s, "\t\tahgidx: %u ahgmode: %u\n", (u8)((desc[1] & SDMA_DESC1_HEADER_INDEX_SMASK) - >> SDMA_DESC1_HEADER_INDEX_MASK), + >> SDMA_DESC1_HEADER_INDEX_SHIFT), (u8)((desc[1] & SDMA_DESC1_HEADER_MODE_SMASK) >> SDMA_DESC1_HEADER_MODE_SHIFT)); head = (head + 1) & sde->sdma_mask; diff --git a/drivers/staging/rdma/hfi1/sdma.h b/drivers/staging/rdma/hfi1/sdma.h index 1e613fcd8f4c..496086903891 100644 --- a/drivers/staging/rdma/hfi1/sdma.h +++ b/drivers/staging/rdma/hfi1/sdma.h @@ -109,53 +109,53 @@ /* * Bits defined in the send DMA descriptor. */ -#define SDMA_DESC0_FIRST_DESC_FLAG (1ULL<<63) -#define SDMA_DESC0_LAST_DESC_FLAG (1ULL<<62) +#define SDMA_DESC0_FIRST_DESC_FLAG (1ULL << 63) +#define SDMA_DESC0_LAST_DESC_FLAG (1ULL << 62) #define SDMA_DESC0_BYTE_COUNT_SHIFT 48 #define SDMA_DESC0_BYTE_COUNT_WIDTH 14 #define SDMA_DESC0_BYTE_COUNT_MASK \ - ((1ULL<<SDMA_DESC0_BYTE_COUNT_WIDTH)-1ULL) + ((1ULL << SDMA_DESC0_BYTE_COUNT_WIDTH) - 1) #define SDMA_DESC0_BYTE_COUNT_SMASK \ - (SDMA_DESC0_BYTE_COUNT_MASK<<SDMA_DESC0_BYTE_COUNT_SHIFT) + (SDMA_DESC0_BYTE_COUNT_MASK << SDMA_DESC0_BYTE_COUNT_SHIFT) #define SDMA_DESC0_PHY_ADDR_SHIFT 0 #define SDMA_DESC0_PHY_ADDR_WIDTH 48 #define SDMA_DESC0_PHY_ADDR_MASK \ - ((1ULL<<SDMA_DESC0_PHY_ADDR_WIDTH)-1ULL) + ((1ULL << SDMA_DESC0_PHY_ADDR_WIDTH) - 1) #define SDMA_DESC0_PHY_ADDR_SMASK \ - (SDMA_DESC0_PHY_ADDR_MASK<<SDMA_DESC0_PHY_ADDR_SHIFT) + (SDMA_DESC0_PHY_ADDR_MASK << SDMA_DESC0_PHY_ADDR_SHIFT) #define SDMA_DESC1_HEADER_UPDATE1_SHIFT 32 #define SDMA_DESC1_HEADER_UPDATE1_WIDTH 32 #define SDMA_DESC1_HEADER_UPDATE1_MASK \ - ((1ULL<<SDMA_DESC1_HEADER_UPDATE1_WIDTH)-1ULL) + ((1ULL << SDMA_DESC1_HEADER_UPDATE1_WIDTH) - 1) #define SDMA_DESC1_HEADER_UPDATE1_SMASK \ - (SDMA_DESC1_HEADER_UPDATE1_MASK<<SDMA_DESC1_HEADER_UPDATE1_SHIFT) + (SDMA_DESC1_HEADER_UPDATE1_MASK << SDMA_DESC1_HEADER_UPDATE1_SHIFT) #define SDMA_DESC1_HEADER_MODE_SHIFT 13 #define SDMA_DESC1_HEADER_MODE_WIDTH 3 #define SDMA_DESC1_HEADER_MODE_MASK \ - ((1ULL<<SDMA_DESC1_HEADER_MODE_WIDTH)-1ULL) + ((1ULL << SDMA_DESC1_HEADER_MODE_WIDTH) - 1) #define SDMA_DESC1_HEADER_MODE_SMASK \ - (SDMA_DESC1_HEADER_MODE_MASK<<SDMA_DESC1_HEADER_MODE_SHIFT) + (SDMA_DESC1_HEADER_MODE_MASK << SDMA_DESC1_HEADER_MODE_SHIFT) #define SDMA_DESC1_HEADER_INDEX_SHIFT 8 #define SDMA_DESC1_HEADER_INDEX_WIDTH 5 #define SDMA_DESC1_HEADER_INDEX_MASK \ - ((1ULL<<SDMA_DESC1_HEADER_INDEX_WIDTH)-1ULL) + ((1ULL << SDMA_DESC1_HEADER_INDEX_WIDTH) - 1) #define SDMA_DESC1_HEADER_INDEX_SMASK \ - (SDMA_DESC1_HEADER_INDEX_MASK<<SDMA_DESC1_HEADER_INDEX_SHIFT) + (SDMA_DESC1_HEADER_INDEX_MASK << SDMA_DESC1_HEADER_INDEX_SHIFT) #define SDMA_DESC1_HEADER_DWS_SHIFT 4 #define SDMA_DESC1_HEADER_DWS_WIDTH 4 #define SDMA_DESC1_HEADER_DWS_MASK \ - ((1ULL<<SDMA_DESC1_HEADER_DWS_WIDTH)-1ULL) + ((1ULL << SDMA_DESC1_HEADER_DWS_WIDTH) - 1) #define SDMA_DESC1_HEADER_DWS_SMASK \ - (SDMA_DESC1_HEADER_DWS_MASK<<SDMA_DESC1_HEADER_DWS_SHIFT) + (SDMA_DESC1_HEADER_DWS_MASK << SDMA_DESC1_HEADER_DWS_SHIFT) #define SDMA_DESC1_GENERATION_SHIFT 2 #define SDMA_DESC1_GENERATION_WIDTH 2 #define SDMA_DESC1_GENERATION_MASK \ - ((1ULL<<SDMA_DESC1_GENERATION_WIDTH)-1ULL) + ((1ULL << SDMA_DESC1_GENERATION_WIDTH) - 1) #define SDMA_DESC1_GENERATION_SMASK \ - (SDMA_DESC1_GENERATION_MASK<<SDMA_DESC1_GENERATION_SHIFT) -#define SDMA_DESC1_INT_REQ_FLAG (1ULL<<1) -#define SDMA_DESC1_HEAD_TO_HOST_FLAG (1ULL<<0) + (SDMA_DESC1_GENERATION_MASK << SDMA_DESC1_GENERATION_SHIFT) +#define SDMA_DESC1_INT_REQ_FLAG (1ULL << 1) +#define SDMA_DESC1_HEAD_TO_HOST_FLAG (1ULL << 0) enum sdma_states { sdma_state_s00_hw_down, diff --git a/drivers/staging/rdma/hfi1/verbs.c b/drivers/staging/rdma/hfi1/verbs.c index 53ac21431542..41bb59eb001c 100644 --- a/drivers/staging/rdma/hfi1/verbs.c +++ b/drivers/staging/rdma/hfi1/verbs.c @@ -749,11 +749,13 @@ static inline struct verbs_txreq *get_txreq(struct hfi1_ibdev *dev, struct verbs_txreq *tx; tx = kmem_cache_alloc(dev->verbs_txreq_cache, GFP_ATOMIC); - if (!tx) + if (!tx) { /* call slow path to get the lock */ tx = __get_txreq(dev, qp); - if (tx) - tx->qp = qp; + if (IS_ERR(tx)) + return tx; + } + tx->qp = qp; return tx; } diff --git a/drivers/staging/unisys/visorbus/Makefile b/drivers/staging/unisys/visorbus/Makefile index fa27ee5f336c..fc790e7592fc 100644 --- a/drivers/staging/unisys/visorbus/Makefile +++ b/drivers/staging/unisys/visorbus/Makefile @@ -10,4 +10,3 @@ visorbus-y += visorchipset.o visorbus-y += periodic_work.o ccflags-y += -Idrivers/staging/unisys/include -ccflags-y += -Idrivers/staging/unisys/visorutil diff --git a/drivers/staging/unisys/visorbus/visorbus_main.c b/drivers/staging/unisys/visorbus/visorbus_main.c index 2309f5f2b238..a272b48bab28 100644 --- a/drivers/staging/unisys/visorbus/visorbus_main.c +++ b/drivers/staging/unisys/visorbus/visorbus_main.c @@ -37,6 +37,8 @@ static int visorbus_debugref; #define POLLJIFFIES_TESTWORK 100 #define POLLJIFFIES_NORMALCHANNEL 10 +static int busreg_rc = -ENODEV; /* stores the result from bus registration */ + static int visorbus_uevent(struct device *xdev, struct kobj_uevent_env *env); static int visorbus_match(struct device *xdev, struct device_driver *xdrv); static void fix_vbus_dev_info(struct visor_device *visordev); @@ -863,6 +865,9 @@ int visorbus_register_visor_driver(struct visor_driver *drv) { int rc = 0; + if (busreg_rc < 0) + return -ENODEV; /*can't register on a nonexistent bus*/ + drv->driver.name = drv->name; drv->driver.bus = &visorbus_type; drv->driver.probe = visordriver_probe_device; @@ -885,6 +890,8 @@ int visorbus_register_visor_driver(struct visor_driver *drv) if (rc < 0) return rc; rc = register_driver_attributes(drv); + if (rc < 0) + driver_unregister(&drv->driver); return rc; } EXPORT_SYMBOL_GPL(visorbus_register_visor_driver); @@ -1260,10 +1267,8 @@ remove_bus_instance(struct visor_device *dev) static int create_bus_type(void) { - int rc = 0; - - rc = bus_register(&visorbus_type); - return rc; + busreg_rc = bus_register(&visorbus_type); + return busreg_rc; } /** Remove the one-and-only one instance of the visor bus type (visorbus_type). diff --git a/drivers/staging/unisys/visornic/visornic_main.c b/drivers/staging/unisys/visornic/visornic_main.c index 8c9da7ea7845..9d3c1e282062 100644 --- a/drivers/staging/unisys/visornic/visornic_main.c +++ b/drivers/staging/unisys/visornic/visornic_main.c @@ -1189,16 +1189,16 @@ visornic_rx(struct uiscmdrsp *cmdrsp) spin_lock_irqsave(&devdata->priv_lock, flags); atomic_dec(&devdata->num_rcvbuf_in_iovm); - /* update rcv stats - call it with priv_lock held */ - devdata->net_stats.rx_packets++; - devdata->net_stats.rx_bytes = skb->len; - /* set length to how much was ACTUALLY received - * NOTE: rcv_done_len includes actual length of data rcvd * including ethhdr */ skb->len = cmdrsp->net.rcv.rcv_done_len; + /* update rcv stats - call it with priv_lock held */ + devdata->net_stats.rx_packets++; + devdata->net_stats.rx_bytes += skb->len; + /* test enabled while holding lock */ if (!(devdata->enabled && devdata->enab_dis_acked)) { /* don't process it unless we're in enable mode and until @@ -1924,13 +1924,16 @@ static int visornic_probe(struct visor_device *dev) "%s debugfs_create_dir %s failed\n", __func__, netdev->name); err = -ENOMEM; - goto cleanup_xmit_cmdrsp; + goto cleanup_register_netdev; } dev_info(&dev->device, "%s success netdev=%s\n", __func__, netdev->name); return 0; +cleanup_register_netdev: + unregister_netdev(netdev); + cleanup_napi_add: del_timer_sync(&devdata->irq_poll_timer); netif_napi_del(&devdata->napi); @@ -2128,8 +2131,9 @@ static int visornic_init(void) if (!dev_num_pool) goto cleanup_workqueue; - visorbus_register_visor_driver(&visornic_driver); - return 0; + err = visorbus_register_visor_driver(&visornic_driver); + if (!err) + return 0; cleanup_workqueue: if (visornic_timeout_reset_workqueue) { diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index fd092909a457..342a07c58d89 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -269,14 +269,14 @@ int iscsit_deaccess_np(struct iscsi_np *np, struct iscsi_portal_group *tpg, } bool iscsit_check_np_match( - struct __kernel_sockaddr_storage *sockaddr, + struct sockaddr_storage *sockaddr, struct iscsi_np *np, int network_transport) { struct sockaddr_in *sock_in, *sock_in_e; struct sockaddr_in6 *sock_in6, *sock_in6_e; bool ip_match = false; - u16 port; + u16 port, port_e; if (sockaddr->ss_family == AF_INET6) { sock_in6 = (struct sockaddr_in6 *)sockaddr; @@ -288,6 +288,7 @@ bool iscsit_check_np_match( ip_match = true; port = ntohs(sock_in6->sin6_port); + port_e = ntohs(sock_in6_e->sin6_port); } else { sock_in = (struct sockaddr_in *)sockaddr; sock_in_e = (struct sockaddr_in *)&np->np_sockaddr; @@ -296,9 +297,10 @@ bool iscsit_check_np_match( ip_match = true; port = ntohs(sock_in->sin_port); + port_e = ntohs(sock_in_e->sin_port); } - if (ip_match && (np->np_port == port) && + if (ip_match && (port_e == port) && (np->np_network_transport == network_transport)) return true; @@ -309,7 +311,7 @@ bool iscsit_check_np_match( * Called with mutex np_lock held */ static struct iscsi_np *iscsit_get_np( - struct __kernel_sockaddr_storage *sockaddr, + struct sockaddr_storage *sockaddr, int network_transport) { struct iscsi_np *np; @@ -340,12 +342,9 @@ static struct iscsi_np *iscsit_get_np( } struct iscsi_np *iscsit_add_np( - struct __kernel_sockaddr_storage *sockaddr, - char *ip_str, + struct sockaddr_storage *sockaddr, int network_transport) { - struct sockaddr_in *sock_in; - struct sockaddr_in6 *sock_in6; struct iscsi_np *np; int ret; @@ -368,16 +367,6 @@ struct iscsi_np *iscsit_add_np( } np->np_flags |= NPF_IP_NETWORK; - if (sockaddr->ss_family == AF_INET6) { - sock_in6 = (struct sockaddr_in6 *)sockaddr; - snprintf(np->np_ip, IPV6_ADDRESS_SPACE, "%s", ip_str); - np->np_port = ntohs(sock_in6->sin6_port); - } else { - sock_in = (struct sockaddr_in *)sockaddr; - sprintf(np->np_ip, "%s", ip_str); - np->np_port = ntohs(sock_in->sin_port); - } - np->np_network_transport = network_transport; spin_lock_init(&np->np_thread_lock); init_completion(&np->np_restart_comp); @@ -411,8 +400,8 @@ struct iscsi_np *iscsit_add_np( list_add_tail(&np->np_list, &g_np_list); mutex_unlock(&np_lock); - pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n", - np->np_ip, np->np_port, np->np_transport->name); + pr_debug("CORE[0] - Added Network Portal: %pISpc on %s\n", + &np->np_sockaddr, np->np_transport->name); return np; } @@ -481,8 +470,8 @@ int iscsit_del_np(struct iscsi_np *np) list_del(&np->np_list); mutex_unlock(&np_lock); - pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n", - np->np_ip, np->np_port, np->np_transport->name); + pr_debug("CORE[0] - Removed Network Portal: %pISpc on %s\n", + &np->np_sockaddr, np->np_transport->name); iscsit_put_transport(np->np_transport); kfree(np); @@ -1209,7 +1198,6 @@ static u32 iscsit_do_crypto_hash_sg( u8 *pad_bytes) { u32 data_crc; - u32 i; struct scatterlist *sg; unsigned int page_off; @@ -1218,15 +1206,15 @@ static u32 iscsit_do_crypto_hash_sg( sg = cmd->first_data_sg; page_off = cmd->first_data_sg_off; - i = 0; while (data_length) { - u32 cur_len = min_t(u32, data_length, (sg[i].length - page_off)); + u32 cur_len = min_t(u32, data_length, (sg->length - page_off)); - crypto_hash_update(hash, &sg[i], cur_len); + crypto_hash_update(hash, sg, cur_len); data_length -= cur_len; page_off = 0; - i++; + /* iscsit_map_iovec has already checked for invalid sg pointers */ + sg = sg_next(sg); } if (padding) { @@ -2556,7 +2544,7 @@ static int iscsit_send_conn_drop_async_message( cmd->stat_sn = conn->stat_sn++; hdr->statsn = cpu_to_be32(cmd->stat_sn); hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); - hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); + hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn)); hdr->async_event = ISCSI_ASYNC_MSG_DROPPING_CONNECTION; hdr->param1 = cpu_to_be16(cmd->logout_cid); hdr->param2 = cpu_to_be16(conn->sess->sess_ops->DefaultTime2Wait); @@ -2628,7 +2616,7 @@ iscsit_build_datain_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn, hdr->statsn = cpu_to_be32(0xFFFFFFFF); hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); - hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); + hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn)); hdr->datasn = cpu_to_be32(datain->data_sn); hdr->offset = cpu_to_be32(datain->offset); @@ -2839,7 +2827,7 @@ iscsit_build_logout_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, iscsit_increment_maxcmdsn(cmd, conn->sess); hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); - hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); + hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn)); pr_debug("Built Logout Response ITT: 0x%08x StatSN:" " 0x%08x Response: 0x%02x CID: %hu on CID: %hu\n", @@ -2902,7 +2890,7 @@ iscsit_build_nopin_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, iscsit_increment_maxcmdsn(cmd, conn->sess); hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); - hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); + hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn)); pr_debug("Built NOPIN %s Response ITT: 0x%08x, TTT: 0x%08x," " StatSN: 0x%08x, Length %u\n", (nopout_response) ? @@ -3049,7 +3037,7 @@ static int iscsit_send_r2t( hdr->ttt = cpu_to_be32(r2t->targ_xfer_tag); hdr->statsn = cpu_to_be32(conn->stat_sn); hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); - hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); + hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn)); hdr->r2tsn = cpu_to_be32(r2t->r2t_sn); hdr->data_offset = cpu_to_be32(r2t->offset); hdr->data_length = cpu_to_be32(r2t->xfer_len); @@ -3202,7 +3190,7 @@ void iscsit_build_rsp_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn, iscsit_increment_maxcmdsn(cmd, conn->sess); hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); - hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); + hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn)); pr_debug("Built SCSI Response, ITT: 0x%08x, StatSN: 0x%08x," " Response: 0x%02x, SAM Status: 0x%02x, CID: %hu\n", @@ -3321,7 +3309,7 @@ iscsit_build_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, iscsit_increment_maxcmdsn(cmd, conn->sess); hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); - hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); + hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn)); pr_debug("Built Task Management Response ITT: 0x%08x," " StatSN: 0x%08x, Response: 0x%02x, CID: %hu\n", @@ -3399,6 +3387,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, int target_name_printed; unsigned char buf[ISCSI_IQN_LEN+12]; /* iqn + "TargetName=" + \0 */ unsigned char *text_in = cmd->text_in_ptr, *text_ptr = NULL; + bool active; buffer_len = min(conn->conn_ops->MaxRecvDataSegmentLength, SENDTARGETS_BUF_LIMIT); @@ -3452,19 +3441,18 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, } spin_lock(&tpg->tpg_state_lock); - if ((tpg->tpg_state == TPG_STATE_FREE) || - (tpg->tpg_state == TPG_STATE_INACTIVE)) { - spin_unlock(&tpg->tpg_state_lock); - continue; - } + active = (tpg->tpg_state == TPG_STATE_ACTIVE); spin_unlock(&tpg->tpg_state_lock); + if (!active && tpg->tpg_attrib.tpg_enabled_sendtargets) + continue; + spin_lock(&tpg->tpg_np_lock); list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) { struct iscsi_np *np = tpg_np->tpg_np; bool inaddr_any = iscsit_check_inaddr_any(np); - char *fmt_str; + struct sockaddr_storage *sockaddr; if (np->np_network_transport != network_transport) continue; @@ -3492,15 +3480,15 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, } } - if (np->np_sockaddr.ss_family == AF_INET6) - fmt_str = "TargetAddress=[%s]:%hu,%hu"; + if (inaddr_any) + sockaddr = &conn->local_sockaddr; else - fmt_str = "TargetAddress=%s:%hu,%hu"; + sockaddr = &np->np_sockaddr; - len = sprintf(buf, fmt_str, - inaddr_any ? conn->local_ip : np->np_ip, - np->np_port, - tpg->tpgt); + len = sprintf(buf, "TargetAddress=" + "%pISpc,%hu", + sockaddr, + tpg->tpgt); len += 1; if ((len + payload_len) > buffer_len) { @@ -3576,7 +3564,7 @@ iscsit_build_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, */ cmd->maxcmdsn_inc = 0; hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); - hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); + hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn)); pr_debug("Built Text Response: ITT: 0x%08x, TTT: 0x%08x, StatSN: 0x%08x," " Length: %u, CID: %hu F: %d C: %d\n", cmd->init_task_tag, @@ -3654,7 +3642,7 @@ iscsit_build_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn, cmd->stat_sn = conn->stat_sn++; hdr->statsn = cpu_to_be32(cmd->stat_sn); hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); - hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); + hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn)); } EXPORT_SYMBOL(iscsit_build_reject); diff --git a/drivers/target/iscsi/iscsi_target.h b/drivers/target/iscsi/iscsi_target.h index 7d0f9c00d9c2..4cf2c0f2ba2f 100644 --- a/drivers/target/iscsi/iscsi_target.h +++ b/drivers/target/iscsi/iscsi_target.h @@ -10,10 +10,10 @@ extern int iscsit_access_np(struct iscsi_np *, struct iscsi_portal_group *); extern void iscsit_login_kref_put(struct kref *); extern int iscsit_deaccess_np(struct iscsi_np *, struct iscsi_portal_group *, struct iscsi_tpg_np *); -extern bool iscsit_check_np_match(struct __kernel_sockaddr_storage *, +extern bool iscsit_check_np_match(struct sockaddr_storage *, struct iscsi_np *, int); -extern struct iscsi_np *iscsit_add_np(struct __kernel_sockaddr_storage *, - char *, int); +extern struct iscsi_np *iscsit_add_np(struct sockaddr_storage *, + int); extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *, struct iscsi_portal_group *, bool); extern int iscsit_del_np(struct iscsi_np *); diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index c1898c84b3d2..c7461d770d3a 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -99,7 +99,7 @@ static ssize_t lio_target_np_store_sctp( * Use existing np->np_sockaddr for SCTP network portal reference */ tpg_np_sctp = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr, - np->np_ip, tpg_np, ISCSI_SCTP_TCP); + tpg_np, ISCSI_SCTP_TCP); if (!tpg_np_sctp || IS_ERR(tpg_np_sctp)) goto out; } else { @@ -177,7 +177,7 @@ static ssize_t lio_target_np_store_iser( } tpg_np_iser = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr, - np->np_ip, tpg_np, ISCSI_INFINIBAND); + tpg_np, ISCSI_INFINIBAND); if (IS_ERR(tpg_np_iser)) { rc = PTR_ERR(tpg_np_iser); goto out; @@ -220,7 +220,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg( struct iscsi_portal_group *tpg; struct iscsi_tpg_np *tpg_np; char *str, *str2, *ip_str, *port_str; - struct __kernel_sockaddr_storage sockaddr; + struct sockaddr_storage sockaddr; struct sockaddr_in *sock_in; struct sockaddr_in6 *sock_in6; unsigned long port; @@ -235,7 +235,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg( memset(buf, 0, MAX_PORTAL_LEN + 1); snprintf(buf, MAX_PORTAL_LEN + 1, "%s", name); - memset(&sockaddr, 0, sizeof(struct __kernel_sockaddr_storage)); + memset(&sockaddr, 0, sizeof(struct sockaddr_storage)); str = strstr(buf, "["); if (str) { @@ -248,8 +248,8 @@ static struct se_tpg_np *lio_target_call_addnptotpg( return ERR_PTR(-EINVAL); } str++; /* Skip over leading "[" */ - *str2 = '\0'; /* Terminate the IPv6 address */ - str2++; /* Skip over the "]" */ + *str2 = '\0'; /* Terminate the unbracketed IPv6 address */ + str2++; /* Skip over the \0 */ port_str = strstr(str2, ":"); if (!port_str) { pr_err("Unable to locate \":port\"" @@ -267,7 +267,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg( sock_in6 = (struct sockaddr_in6 *)&sockaddr; sock_in6->sin6_family = AF_INET6; sock_in6->sin6_port = htons((unsigned short)port); - ret = in6_pton(str, IPV6_ADDRESS_SPACE, + ret = in6_pton(str, -1, (void *)&sock_in6->sin6_addr.in6_u, -1, &end); if (ret <= 0) { pr_err("in6_pton returned: %d\n", ret); @@ -316,7 +316,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg( * sys/kernel/config/iscsi/$IQN/$TPG/np/$IP:$PORT/ * */ - tpg_np = iscsit_tpg_add_network_portal(tpg, &sockaddr, str, NULL, + tpg_np = iscsit_tpg_add_network_portal(tpg, &sockaddr, NULL, ISCSI_TCP); if (IS_ERR(tpg_np)) { iscsit_put_tpg(tpg); @@ -344,8 +344,8 @@ static void lio_target_call_delnpfromtpg( se_tpg = &tpg->tpg_se_tpg; pr_debug("LIO_Target_ConfigFS: DEREGISTER -> %s TPGT: %hu" - " PORTAL: %s:%hu\n", config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item), - tpg->tpgt, tpg_np->tpg_np->np_ip, tpg_np->tpg_np->np_port); + " PORTAL: %pISpc\n", config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item), + tpg->tpgt, &tpg_np->tpg_np->np_sockaddr); ret = iscsit_tpg_del_network_portal(tpg, tpg_np); if (ret < 0) @@ -656,6 +656,7 @@ static ssize_t lio_target_nacl_show_info( struct iscsi_conn *conn; struct se_session *se_sess; ssize_t rb = 0; + u32 max_cmd_sn; spin_lock_bh(&se_nacl->nacl_sess_lock); se_sess = se_nacl->nacl_sess; @@ -703,11 +704,12 @@ static ssize_t lio_target_nacl_show_info( " Values]-----------------------\n"); rb += sprintf(page+rb, " CmdSN/WR : CmdSN/WC : ExpCmdSN" " : MaxCmdSN : ITT : TTT\n"); + max_cmd_sn = (u32) atomic_read(&sess->max_cmd_sn); rb += sprintf(page+rb, " 0x%08x 0x%08x 0x%08x 0x%08x" " 0x%08x 0x%08x\n", sess->cmdsn_window, - (sess->max_cmd_sn - sess->exp_cmd_sn) + 1, - sess->exp_cmd_sn, sess->max_cmd_sn, + (max_cmd_sn - sess->exp_cmd_sn) + 1, + sess->exp_cmd_sn, max_cmd_sn, sess->init_task_tag, sess->targ_xfer_tag); rb += sprintf(page+rb, "----------------------[iSCSI" " Connections]-------------------------\n"); @@ -751,7 +753,7 @@ static ssize_t lio_target_nacl_show_info( break; } - rb += sprintf(page+rb, " Address %s %s", conn->login_ip, + rb += sprintf(page+rb, " Address %pISc %s", &conn->login_sockaddr, (conn->network_transport == ISCSI_TCP) ? "TCP" : "SCTP"); rb += sprintf(page+rb, " StatSN: 0x%08x\n", @@ -1010,6 +1012,11 @@ TPG_ATTR(t10_pi, S_IRUGO | S_IWUSR); */ DEF_TPG_ATTRIB(fabric_prot_type); TPG_ATTR(fabric_prot_type, S_IRUGO | S_IWUSR); +/* + * Define iscsi_tpg_attrib_s_tpg_enabled_sendtargets + */ +DEF_TPG_ATTRIB(tpg_enabled_sendtargets); +TPG_ATTR(tpg_enabled_sendtargets, S_IRUGO | S_IWUSR); static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { &iscsi_tpg_attrib_authentication.attr, @@ -1024,6 +1031,7 @@ static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { &iscsi_tpg_attrib_default_erl.attr, &iscsi_tpg_attrib_t10_pi.attr, &iscsi_tpg_attrib_fabric_prot_type.attr, + &iscsi_tpg_attrib_tpg_enabled_sendtargets.attr, NULL, }; diff --git a/drivers/target/iscsi/iscsi_target_device.c b/drivers/target/iscsi/iscsi_target_device.c index 5fabcd3d623f..0382fa24b53b 100644 --- a/drivers/target/iscsi/iscsi_target_device.c +++ b/drivers/target/iscsi/iscsi_target_device.c @@ -47,19 +47,19 @@ void iscsit_determine_maxcmdsn(struct iscsi_session *sess) * core_set_queue_depth_for_node(). */ sess->cmdsn_window = se_nacl->queue_depth; - sess->max_cmd_sn = (sess->max_cmd_sn + se_nacl->queue_depth) - 1; + atomic_add(se_nacl->queue_depth - 1, &sess->max_cmd_sn); } void iscsit_increment_maxcmdsn(struct iscsi_cmd *cmd, struct iscsi_session *sess) { + u32 max_cmd_sn; + if (cmd->immediate_cmd || cmd->maxcmdsn_inc) return; cmd->maxcmdsn_inc = 1; - mutex_lock(&sess->cmdsn_mutex); - sess->max_cmd_sn += 1; - pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn); - mutex_unlock(&sess->cmdsn_mutex); + max_cmd_sn = atomic_inc_return(&sess->max_cmd_sn); + pr_debug("Updated MaxCmdSN to 0x%08x\n", max_cmd_sn); } EXPORT_SYMBOL(iscsit_increment_maxcmdsn); diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 7e8f65e5448f..96e78c823d13 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -331,7 +331,7 @@ static int iscsi_login_zero_tsih_s1( * The FFP CmdSN window values will be allocated from the TPG's * Initiator Node's ACL once the login has been successfully completed. */ - sess->max_cmd_sn = be32_to_cpu(pdu->cmdsn); + atomic_set(&sess->max_cmd_sn, be32_to_cpu(pdu->cmdsn)); sess->sess_ops = kzalloc(sizeof(struct iscsi_sess_ops), GFP_KERNEL); if (!sess->sess_ops) { @@ -729,9 +729,9 @@ void iscsi_post_login_handler( stop_timer = 1; } - pr_debug("iSCSI Login successful on CID: %hu from %s to" - " %s:%hu,%hu\n", conn->cid, conn->login_ip, - conn->local_ip, conn->local_port, tpg->tpgt); + pr_debug("iSCSI Login successful on CID: %hu from %pISpc to" + " %pISpc,%hu\n", conn->cid, &conn->login_sockaddr, + &conn->local_sockaddr, tpg->tpgt); list_add_tail(&conn->conn_list, &sess->sess_conn_list); atomic_inc(&sess->nconn); @@ -776,8 +776,8 @@ void iscsi_post_login_handler( pr_debug("Moving to TARG_SESS_STATE_LOGGED_IN.\n"); sess->session_state = TARG_SESS_STATE_LOGGED_IN; - pr_debug("iSCSI Login successful on CID: %hu from %s to %s:%hu,%hu\n", - conn->cid, conn->login_ip, conn->local_ip, conn->local_port, + pr_debug("iSCSI Login successful on CID: %hu from %pISpc to %pISpc,%hu\n", + conn->cid, &conn->login_sockaddr, &conn->local_sockaddr, tpg->tpgt); spin_lock_bh(&sess->conn_lock); @@ -823,8 +823,8 @@ static void iscsi_handle_login_thread_timeout(unsigned long data) struct iscsi_np *np = (struct iscsi_np *) data; spin_lock_bh(&np->np_thread_lock); - pr_err("iSCSI Login timeout on Network Portal %s:%hu\n", - np->np_ip, np->np_port); + pr_err("iSCSI Login timeout on Network Portal %pISpc\n", + &np->np_sockaddr); if (np->np_login_timer_flags & ISCSI_TF_STOP) { spin_unlock_bh(&np->np_thread_lock); @@ -877,7 +877,7 @@ static void iscsi_stop_login_thread_timer(struct iscsi_np *np) int iscsit_setup_np( struct iscsi_np *np, - struct __kernel_sockaddr_storage *sockaddr) + struct sockaddr_storage *sockaddr) { struct socket *sock = NULL; int backlog = ISCSIT_TCP_BACKLOG, ret, opt = 0, len; @@ -916,7 +916,7 @@ int iscsit_setup_np( * in iscsi_target_configfs.c code.. */ memcpy(&np->np_sockaddr, sockaddr, - sizeof(struct __kernel_sockaddr_storage)); + sizeof(struct sockaddr_storage)); if (sockaddr->ss_family == AF_INET6) len = sizeof(struct sockaddr_in6); @@ -975,7 +975,7 @@ fail: int iscsi_target_setup_login_socket( struct iscsi_np *np, - struct __kernel_sockaddr_storage *sockaddr) + struct sockaddr_storage *sockaddr) { struct iscsit_transport *t; int rc; @@ -1015,44 +1015,42 @@ int iscsit_accept_np(struct iscsi_np *np, struct iscsi_conn *conn) rc = conn->sock->ops->getname(conn->sock, (struct sockaddr *)&sock_in6, &err, 1); if (!rc) { - if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr)) - snprintf(conn->login_ip, sizeof(conn->login_ip), "[%pI6c]", - &sock_in6.sin6_addr.in6_u); - else - snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI4", - &sock_in6.sin6_addr.s6_addr32[3]); - conn->login_port = ntohs(sock_in6.sin6_port); + if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr)) { + memcpy(&conn->login_sockaddr, &sock_in6, sizeof(sock_in6)); + } else { + /* Pretend to be an ipv4 socket */ + sock_in.sin_family = AF_INET; + sock_in.sin_port = sock_in6.sin6_port; + memcpy(&sock_in.sin_addr, &sock_in6.sin6_addr.s6_addr32[3], 4); + memcpy(&conn->login_sockaddr, &sock_in, sizeof(sock_in)); + } } rc = conn->sock->ops->getname(conn->sock, (struct sockaddr *)&sock_in6, &err, 0); if (!rc) { - if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr)) - snprintf(conn->local_ip, sizeof(conn->local_ip), "[%pI6c]", - &sock_in6.sin6_addr.in6_u); - else - snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI4", - &sock_in6.sin6_addr.s6_addr32[3]); - conn->local_port = ntohs(sock_in6.sin6_port); + if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr)) { + memcpy(&conn->local_sockaddr, &sock_in6, sizeof(sock_in6)); + } else { + /* Pretend to be an ipv4 socket */ + sock_in.sin_family = AF_INET; + sock_in.sin_port = sock_in6.sin6_port; + memcpy(&sock_in.sin_addr, &sock_in6.sin6_addr.s6_addr32[3], 4); + memcpy(&conn->local_sockaddr, &sock_in, sizeof(sock_in)); + } } } else { memset(&sock_in, 0, sizeof(struct sockaddr_in)); rc = conn->sock->ops->getname(conn->sock, (struct sockaddr *)&sock_in, &err, 1); - if (!rc) { - sprintf(conn->login_ip, "%pI4", - &sock_in.sin_addr.s_addr); - conn->login_port = ntohs(sock_in.sin_port); - } + if (!rc) + memcpy(&conn->login_sockaddr, &sock_in, sizeof(sock_in)); rc = conn->sock->ops->getname(conn->sock, (struct sockaddr *)&sock_in, &err, 0); - if (!rc) { - sprintf(conn->local_ip, "%pI4", - &sock_in.sin_addr.s_addr); - conn->local_port = ntohs(sock_in.sin_port); - } + if (!rc) + memcpy(&conn->local_sockaddr, &sock_in, sizeof(sock_in)); } return 0; @@ -1302,8 +1300,8 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) spin_lock_bh(&np->np_thread_lock); if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { spin_unlock_bh(&np->np_thread_lock); - pr_err("iSCSI Network Portal on %s:%hu currently not" - " active.\n", np->np_ip, np->np_port); + pr_err("iSCSI Network Portal on %pISpc currently not" + " active.\n", &np->np_sockaddr); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); goto new_sess_out; @@ -1312,9 +1310,9 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) conn->network_transport = np->np_network_transport; - pr_debug("Received iSCSI login request from %s on %s Network" - " Portal %s:%hu\n", conn->login_ip, np->np_transport->name, - conn->local_ip, conn->local_port); + pr_debug("Received iSCSI login request from %pISpc on %s Network" + " Portal %pISpc\n", &conn->login_sockaddr, np->np_transport->name, + &conn->local_sockaddr); pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n"); conn->conn_state = TARG_CONN_STATE_IN_LOGIN; diff --git a/drivers/target/iscsi/iscsi_target_login.h b/drivers/target/iscsi/iscsi_target_login.h index 57aa0d0fd820..b597aa2c61a1 100644 --- a/drivers/target/iscsi/iscsi_target_login.h +++ b/drivers/target/iscsi/iscsi_target_login.h @@ -5,9 +5,9 @@ extern int iscsi_login_setup_crypto(struct iscsi_conn *); extern int iscsi_check_for_session_reinstatement(struct iscsi_conn *); extern int iscsi_login_post_auth_non_zero_tsih(struct iscsi_conn *, u16, u32); extern int iscsit_setup_np(struct iscsi_np *, - struct __kernel_sockaddr_storage *); + struct sockaddr_storage *); extern int iscsi_target_setup_login_socket(struct iscsi_np *, - struct __kernel_sockaddr_storage *); + struct sockaddr_storage *); extern int iscsit_accept_np(struct iscsi_np *, struct iscsi_conn *); extern int iscsit_get_login_rx(struct iscsi_conn *, struct iscsi_login *); extern int iscsit_put_login_tx(struct iscsi_conn *, struct iscsi_login *, u32); diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index f9cde9141836..5c964c09c89f 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -341,7 +341,6 @@ static int iscsi_target_check_first_request( static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_login *login) { u32 padding = 0; - struct iscsi_session *sess = conn->sess; struct iscsi_login_rsp *login_rsp; login_rsp = (struct iscsi_login_rsp *) login->rsp; @@ -353,7 +352,7 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log login_rsp->itt = login->init_task_tag; login_rsp->statsn = cpu_to_be32(conn->stat_sn++); login_rsp->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); - login_rsp->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); + login_rsp->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn)); pr_debug("Sending Login Response, Flags: 0x%02x, ITT: 0x%08x," " ExpCmdSN; 0x%08x, MaxCmdSN: 0x%08x, StatSN: 0x%08x, Length:" @@ -382,10 +381,6 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log goto err; login->rsp_length = 0; - mutex_lock(&sess->cmdsn_mutex); - login_rsp->exp_cmdsn = cpu_to_be32(sess->exp_cmd_sn); - login_rsp->max_cmdsn = cpu_to_be32(sess->max_cmd_sn); - mutex_unlock(&sess->cmdsn_mutex); return 0; diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index e8a52f7d6204..51d1734d5390 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -407,6 +407,7 @@ int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr) TYPERANGE_UTF8, USE_INITIAL_ONLY); if (!param) goto out; + /* * Extra parameters for ISER from RFC-5046 */ @@ -496,9 +497,9 @@ int iscsi_set_keys_to_negotiate( } else if (!strcmp(param->name, SESSIONTYPE)) { SET_PSTATE_NEGOTIATE(param); } else if (!strcmp(param->name, IFMARKER)) { - SET_PSTATE_NEGOTIATE(param); + SET_PSTATE_REJECT(param); } else if (!strcmp(param->name, OFMARKER)) { - SET_PSTATE_NEGOTIATE(param); + SET_PSTATE_REJECT(param); } else if (!strcmp(param->name, IFMARKINT)) { SET_PSTATE_REJECT(param); } else if (!strcmp(param->name, OFMARKINT)) { diff --git a/drivers/target/iscsi/iscsi_target_stat.c b/drivers/target/iscsi/iscsi_target_stat.c index 5e1349a3b143..9dd94ff0b62c 100644 --- a/drivers/target/iscsi/iscsi_target_stat.c +++ b/drivers/target/iscsi/iscsi_target_stat.c @@ -430,7 +430,7 @@ static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr( int ret; spin_lock(&lstat->lock); - ret = snprintf(page, PAGE_SIZE, "%s\n", lstat->last_intr_fail_ip_addr); + ret = snprintf(page, PAGE_SIZE, "%pISc\n", &lstat->last_intr_fail_sockaddr); spin_unlock(&lstat->lock); return ret; diff --git a/drivers/target/iscsi/iscsi_target_tmr.c b/drivers/target/iscsi/iscsi_target_tmr.c index cf59c397007b..11320df939f7 100644 --- a/drivers/target/iscsi/iscsi_target_tmr.c +++ b/drivers/target/iscsi/iscsi_target_tmr.c @@ -50,7 +50,7 @@ u8 iscsit_tmr_abort_task( pr_err("Unable to locate RefTaskTag: 0x%08x on CID:" " %hu.\n", hdr->rtt, conn->cid); return (iscsi_sna_gte(be32_to_cpu(hdr->refcmdsn), conn->sess->exp_cmd_sn) && - iscsi_sna_lte(be32_to_cpu(hdr->refcmdsn), conn->sess->max_cmd_sn)) ? + iscsi_sna_lte(be32_to_cpu(hdr->refcmdsn), (u32) atomic_read(&conn->sess->max_cmd_sn))) ? ISCSI_TMF_RSP_COMPLETE : ISCSI_TMF_RSP_NO_TASK; } if (ref_cmd->cmd_sn != be32_to_cpu(hdr->refcmdsn)) { diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index 968068ffcb1c..23c95cd14167 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -226,6 +226,7 @@ static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg) a->default_erl = TA_DEFAULT_ERL; a->t10_pi = TA_DEFAULT_T10_PI; a->fabric_prot_type = TA_DEFAULT_FABRIC_PROT_TYPE; + a->tpg_enabled_sendtargets = TA_DEFAULT_TPG_ENABLED_SENDTARGETS; } int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg) @@ -430,7 +431,7 @@ struct iscsi_tpg_np *iscsit_tpg_locate_child_np( static bool iscsit_tpg_check_network_portal( struct iscsi_tiqn *tiqn, - struct __kernel_sockaddr_storage *sockaddr, + struct sockaddr_storage *sockaddr, int network_transport) { struct iscsi_portal_group *tpg; @@ -459,8 +460,7 @@ static bool iscsit_tpg_check_network_portal( struct iscsi_tpg_np *iscsit_tpg_add_network_portal( struct iscsi_portal_group *tpg, - struct __kernel_sockaddr_storage *sockaddr, - char *ip_str, + struct sockaddr_storage *sockaddr, struct iscsi_tpg_np *tpg_np_parent, int network_transport) { @@ -470,8 +470,8 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal( if (!tpg_np_parent) { if (iscsit_tpg_check_network_portal(tpg->tpg_tiqn, sockaddr, network_transport)) { - pr_err("Network Portal: %s already exists on a" - " different TPG on %s\n", ip_str, + pr_err("Network Portal: %pISc already exists on a" + " different TPG on %s\n", sockaddr, tpg->tpg_tiqn->tiqn); return ERR_PTR(-EEXIST); } @@ -484,7 +484,7 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal( return ERR_PTR(-ENOMEM); } - np = iscsit_add_np(sockaddr, ip_str, network_transport); + np = iscsit_add_np(sockaddr, network_transport); if (IS_ERR(np)) { kfree(tpg_np); return ERR_CAST(np); @@ -514,8 +514,8 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal( spin_unlock(&tpg_np_parent->tpg_np_parent_lock); } - pr_debug("CORE[%s] - Added Network Portal: %s:%hu,%hu on %s\n", - tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt, + pr_debug("CORE[%s] - Added Network Portal: %pISpc,%hu on %s\n", + tpg->tpg_tiqn->tiqn, &np->np_sockaddr, tpg->tpgt, np->np_transport->name); return tpg_np; @@ -528,8 +528,8 @@ static int iscsit_tpg_release_np( { iscsit_clear_tpg_np_login_thread(tpg_np, tpg, true); - pr_debug("CORE[%s] - Removed Network Portal: %s:%hu,%hu on %s\n", - tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt, + pr_debug("CORE[%s] - Removed Network Portal: %pISpc,%hu on %s\n", + tpg->tpg_tiqn->tiqn, &np->np_sockaddr, tpg->tpgt, np->np_transport->name); tpg_np->tpg_np = NULL; @@ -892,3 +892,21 @@ int iscsit_ta_fabric_prot_type( return 0; } + +int iscsit_ta_tpg_enabled_sendtargets( + struct iscsi_portal_group *tpg, + u32 flag) +{ + struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; + + if ((flag != 0) && (flag != 1)) { + pr_err("Illegal value %d\n", flag); + return -EINVAL; + } + + a->tpg_enabled_sendtargets = flag; + pr_debug("iSCSI_TPG[%hu] - TPG enabled bit required for SendTargets:" + " %s\n", tpg->tpgt, (a->tpg_enabled_sendtargets) ? "ON" : "OFF"); + + return 0; +} diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h index 95ff5bdecd71..9db32bd24cd4 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.h +++ b/drivers/target/iscsi/iscsi_target_tpg.h @@ -22,7 +22,7 @@ extern struct iscsi_node_attrib *iscsit_tpg_get_node_attrib(struct iscsi_session extern void iscsit_tpg_del_external_nps(struct iscsi_tpg_np *); extern struct iscsi_tpg_np *iscsit_tpg_locate_child_np(struct iscsi_tpg_np *, int); extern struct iscsi_tpg_np *iscsit_tpg_add_network_portal(struct iscsi_portal_group *, - struct __kernel_sockaddr_storage *, char *, struct iscsi_tpg_np *, + struct sockaddr_storage *, struct iscsi_tpg_np *, int); extern int iscsit_tpg_del_network_portal(struct iscsi_portal_group *, struct iscsi_tpg_np *); @@ -40,5 +40,6 @@ extern int iscsit_ta_demo_mode_discovery(struct iscsi_portal_group *, u32); extern int iscsit_ta_default_erl(struct iscsi_portal_group *, u32); extern int iscsit_ta_t10_pi(struct iscsi_portal_group *, u32); extern int iscsit_ta_fabric_prot_type(struct iscsi_portal_group *, u32); +extern int iscsit_ta_tpg_enabled_sendtargets(struct iscsi_portal_group *, u32); #endif /* ISCSI_TARGET_TPG_H */ diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index a2bff0702eb2..428b0d9e3dba 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -233,6 +233,7 @@ struct iscsi_r2t *iscsit_get_holder_for_r2tsn( static inline int iscsit_check_received_cmdsn(struct iscsi_session *sess, u32 cmdsn) { + u32 max_cmdsn; int ret; /* @@ -241,10 +242,10 @@ static inline int iscsit_check_received_cmdsn(struct iscsi_session *sess, u32 cm * or order CmdSNs due to multiple connection sessions and/or * CRC failures. */ - if (iscsi_sna_gt(cmdsn, sess->max_cmd_sn)) { + max_cmdsn = atomic_read(&sess->max_cmd_sn); + if (iscsi_sna_gt(cmdsn, max_cmdsn)) { pr_err("Received CmdSN: 0x%08x is greater than" - " MaxCmdSN: 0x%08x, ignoring.\n", cmdsn, - sess->max_cmd_sn); + " MaxCmdSN: 0x%08x, ignoring.\n", cmdsn, max_cmdsn); ret = CMDSN_MAXCMDSN_OVERRUN; } else if (cmdsn == sess->exp_cmd_sn) { @@ -1371,6 +1372,33 @@ int tx_data( return iscsit_do_tx_data(conn, &c); } +static bool sockaddr_equal(struct sockaddr_storage *x, struct sockaddr_storage *y) +{ + switch (x->ss_family) { + case AF_INET: { + struct sockaddr_in *sinx = (struct sockaddr_in *)x; + struct sockaddr_in *siny = (struct sockaddr_in *)y; + if (sinx->sin_addr.s_addr != siny->sin_addr.s_addr) + return false; + if (sinx->sin_port != siny->sin_port) + return false; + break; + } + case AF_INET6: { + struct sockaddr_in6 *sinx = (struct sockaddr_in6 *)x; + struct sockaddr_in6 *siny = (struct sockaddr_in6 *)y; + if (!ipv6_addr_equal(&sinx->sin6_addr, &siny->sin6_addr)) + return false; + if (sinx->sin6_port != siny->sin6_port) + return false; + break; + } + default: + return false; + } + return true; +} + void iscsit_collect_login_stats( struct iscsi_conn *conn, u8 status_class, @@ -1387,7 +1415,7 @@ void iscsit_collect_login_stats( ls = &tiqn->login_stats; spin_lock(&ls->lock); - if (!strcmp(conn->login_ip, ls->last_intr_fail_ip_addr) && + if (sockaddr_equal(&conn->login_sockaddr, &ls->last_intr_fail_sockaddr) && ((get_jiffies_64() - ls->last_fail_time) < 10)) { /* We already have the failure info for this login */ spin_unlock(&ls->lock); @@ -1427,8 +1455,7 @@ void iscsit_collect_login_stats( ls->last_intr_fail_ip_family = conn->login_family; - snprintf(ls->last_intr_fail_ip_addr, IPV6_ADDRESS_SPACE, - "%s", conn->login_ip); + ls->last_intr_fail_sockaddr = conn->login_sockaddr; ls->last_fail_time = get_jiffies_64(); } diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index a556bdebd775..5bc85ffed720 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -526,7 +526,7 @@ static inline struct tcm_loop_tpg *tl_tpg(struct se_portal_group *se_tpg) static char *tcm_loop_get_endpoint_wwn(struct se_portal_group *se_tpg) { /* - * Return the passed NAA identifier for the SAS Target Port + * Return the passed NAA identifier for the Target Port */ return &tl_tpg(se_tpg)->tl_hba->tl_wwn_address[0]; } @@ -845,7 +845,7 @@ static int tcm_loop_make_nexus( transport_free_session(tl_nexus->se_sess); goto out; } - /* Now, register the SAS I_T Nexus as active. */ + /* Now, register the I_T Nexus as active. */ transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl, tl_nexus->se_sess, tl_nexus); tl_tpg->tl_nexus = tl_nexus; @@ -884,7 +884,7 @@ static int tcm_loop_drop_nexus( " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tpg->tl_hba), tl_nexus->se_sess->se_node_acl->initiatorname); /* - * Release the SCSI I_T Nexus to the emulated SAS Target Port + * Release the SCSI I_T Nexus to the emulated Target Port */ transport_deregister_session(tl_nexus->se_sess); tpg->tl_nexus = NULL; @@ -1034,6 +1034,11 @@ static ssize_t tcm_loop_tpg_store_transport_status( } if (!strncmp(page, "offline", 7)) { tl_tpg->tl_transport_status = TCM_TRANSPORT_OFFLINE; + if (tl_tpg->tl_nexus) { + struct se_session *tl_sess = tl_tpg->tl_nexus->se_sess; + + core_allocate_nexus_loss_ua(tl_sess->se_node_acl); + } return count; } return -EINVAL; @@ -1077,7 +1082,7 @@ static struct se_portal_group *tcm_loop_make_naa_tpg( tl_tpg->tl_hba = tl_hba; tl_tpg->tl_tpgt = tpgt; /* - * Register the tl_tpg as a emulated SAS TCM Target Endpoint + * Register the tl_tpg as a emulated TCM Target Endpoint */ ret = core_tpg_register(wwn, &tl_tpg->tl_se_tpg, tl_hba->tl_proto_id); if (ret < 0) @@ -1102,11 +1107,11 @@ static void tcm_loop_drop_naa_tpg( tl_hba = tl_tpg->tl_hba; tpgt = tl_tpg->tl_tpgt; /* - * Release the I_T Nexus for the Virtual SAS link if present + * Release the I_T Nexus for the Virtual target link if present */ tcm_loop_drop_nexus(tl_tpg); /* - * Deregister the tl_tpg as a emulated SAS TCM Target Endpoint + * Deregister the tl_tpg as a emulated TCM Target Endpoint */ core_tpg_deregister(se_tpg); @@ -1199,8 +1204,9 @@ static void tcm_loop_drop_scsi_hba( struct tcm_loop_hba, tl_hba_wwn); pr_debug("TCM_Loop_ConfigFS: Deallocating emulated Target" - " SAS Address: %s at Linux/SCSI Host ID: %d\n", - tl_hba->tl_wwn_address, tl_hba->sh->host_no); + " %s Address: %s at Linux/SCSI Host ID: %d\n", + tcm_loop_dump_proto_id(tl_hba), tl_hba->tl_wwn_address, + tl_hba->sh->host_no); /* * Call device_unregister() on the original tl_hba->dev. * tcm_loop_fabric_scsi.c:tcm_loop_release_adapter() will diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 09e682b1c549..88ea4e4f124b 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -62,22 +62,13 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) struct se_session *se_sess = se_cmd->se_sess; struct se_node_acl *nacl = se_sess->se_node_acl; struct se_dev_entry *deve; + sense_reason_t ret = TCM_NO_SENSE; rcu_read_lock(); deve = target_nacl_find_deve(nacl, unpacked_lun); if (deve) { atomic_long_inc(&deve->total_cmds); - if ((se_cmd->data_direction == DMA_TO_DEVICE) && - (deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY)) { - pr_err("TARGET_CORE[%s]: Detected WRITE_PROTECTED LUN" - " Access for 0x%08llx\n", - se_cmd->se_tfo->get_fabric_name(), - unpacked_lun); - rcu_read_unlock(); - return TCM_WRITE_PROTECTED; - } - if (se_cmd->data_direction == DMA_TO_DEVICE) atomic_long_add(se_cmd->data_length, &deve->write_bytes); @@ -93,6 +84,17 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) percpu_ref_get(&se_lun->lun_ref); se_cmd->lun_ref_active = true; + + if ((se_cmd->data_direction == DMA_TO_DEVICE) && + (deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY)) { + pr_err("TARGET_CORE[%s]: Detected WRITE_PROTECTED LUN" + " Access for 0x%08llx\n", + se_cmd->se_tfo->get_fabric_name(), + unpacked_lun); + rcu_read_unlock(); + ret = TCM_WRITE_PROTECTED; + goto ref_dev; + } } rcu_read_unlock(); @@ -109,12 +111,6 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) unpacked_lun); return TCM_NON_EXISTENT_LUN; } - /* - * Force WRITE PROTECT for virtual LUN 0 - */ - if ((se_cmd->data_direction != DMA_FROM_DEVICE) && - (se_cmd->data_direction != DMA_NONE)) - return TCM_WRITE_PROTECTED; se_lun = se_sess->se_tpg->tpg_virt_lun0; se_cmd->se_lun = se_sess->se_tpg->tpg_virt_lun0; @@ -123,6 +119,15 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) percpu_ref_get(&se_lun->lun_ref); se_cmd->lun_ref_active = true; + + /* + * Force WRITE PROTECT for virtual LUN 0 + */ + if ((se_cmd->data_direction != DMA_FROM_DEVICE) && + (se_cmd->data_direction != DMA_NONE)) { + ret = TCM_WRITE_PROTECTED; + goto ref_dev; + } } /* * RCU reference protected by percpu se_lun->lun_ref taken above that @@ -130,6 +135,7 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) * pointer can be kfree_rcu() by the final se_lun->lun_group put via * target_core_fabric_configfs.c:target_fabric_port_release */ +ref_dev: se_cmd->se_dev = rcu_dereference_raw(se_lun->lun_se_dev); atomic_long_inc(&se_cmd->se_dev->num_cmds); @@ -140,7 +146,7 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) atomic_long_add(se_cmd->data_length, &se_cmd->se_dev->read_bytes); - return 0; + return ret; } EXPORT_SYMBOL(transport_lookup_cmd_lun); @@ -427,8 +433,6 @@ void core_disable_device_list_for_node( hlist_del_rcu(&orig->link); clear_bit(DEF_PR_REG_ACTIVE, &orig->deve_flags); - rcu_assign_pointer(orig->se_lun, NULL); - rcu_assign_pointer(orig->se_lun_acl, NULL); orig->lun_flags = 0; orig->creation_time = 0; orig->attach_count--; @@ -439,6 +443,9 @@ void core_disable_device_list_for_node( kref_put(&orig->pr_kref, target_pr_kref_release); wait_for_completion(&orig->pr_comp); + rcu_assign_pointer(orig->se_lun, NULL); + rcu_assign_pointer(orig->se_lun_acl, NULL); + kfree_rcu(orig, rcu_head); core_scsi3_free_pr_reg_from_nacl(dev, nacl); @@ -620,8 +627,6 @@ struct se_lun_acl *core_dev_init_initiator_node_lun_acl( lacl->mapped_lun = mapped_lun; lacl->se_lun_nacl = nacl; - snprintf(lacl->initiatorname, TRANSPORT_IQN_LEN, "%s", - nacl->initiatorname); return lacl; } @@ -656,7 +661,7 @@ int core_dev_add_initiator_node_lun_acl( " InitiatorNode: %s\n", tpg->se_tpg_tfo->get_fabric_name(), tpg->se_tpg_tfo->tpg_get_tag(tpg), lun->unpacked_lun, lacl->mapped_lun, (lun_access & TRANSPORT_LUNFLAGS_READ_WRITE) ? "RW" : "RO", - lacl->initiatorname); + nacl->initiatorname); /* * Check to see if there are any existing persistent reservation APTPL * pre-registrations that need to be enabled for this LUN ACL.. @@ -688,7 +693,7 @@ int core_dev_del_initiator_node_lun_acl( " InitiatorNode: %s Mapped LUN: %llu\n", tpg->se_tpg_tfo->get_fabric_name(), tpg->se_tpg_tfo->tpg_get_tag(tpg), lun->unpacked_lun, - lacl->initiatorname, lacl->mapped_lun); + nacl->initiatorname, lacl->mapped_lun); return 0; } @@ -701,7 +706,7 @@ void core_dev_free_initiator_node_lun_acl( " Mapped LUN: %llu\n", tpg->se_tpg_tfo->get_fabric_name(), tpg->se_tpg_tfo->tpg_get_tag(tpg), tpg->se_tpg_tfo->get_fabric_name(), - lacl->initiatorname, lacl->mapped_lun); + lacl->se_lun_nacl->initiatorname, lacl->mapped_lun); kfree(lacl); } @@ -754,7 +759,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) dev->dev_link_magic = SE_DEV_LINK_MAGIC; dev->se_hba = hba; dev->transport = hba->backend->ops; - dev->prot_length = sizeof(struct se_dif_v1_tuple); + dev->prot_length = sizeof(struct t10_pi_tuple); dev->hba_index = hba->hba_index; INIT_LIST_HEAD(&dev->dev_list); @@ -771,7 +776,6 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) spin_lock_init(&dev->se_tmr_lock); spin_lock_init(&dev->qf_cmd_lock); sema_init(&dev->caw_sem, 1); - atomic_set(&dev->dev_ordered_id, 0); INIT_LIST_HEAD(&dev->t10_wwn.t10_vpd_list); spin_lock_init(&dev->t10_wwn.t10_vpd_lock); INIT_LIST_HEAD(&dev->t10_pr.registration_list); diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 48a36989c1a6..be42429468e2 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -203,7 +203,7 @@ static ssize_t target_fabric_mappedlun_store_write_protect( pr_debug("%s_ConfigFS: Changed Initiator ACL: %s" " Mapped LUN: %llu Write Protect bit to %s\n", se_tpg->se_tpg_tfo->get_fabric_name(), - lacl->initiatorname, lacl->mapped_lun, (op) ? "ON" : "OFF"); + se_nacl->initiatorname, lacl->mapped_lun, (op) ? "ON" : "OFF"); return count; diff --git a/drivers/target/target_core_hba.c b/drivers/target/target_core_hba.c index be9cefc07407..22390e0e046c 100644 --- a/drivers/target/target_core_hba.c +++ b/drivers/target/target_core_hba.c @@ -184,3 +184,8 @@ core_delete_hba(struct se_hba *hba) kfree(hba); return 0; } + +bool target_sense_desc_format(struct se_device *dev) +{ + return (dev) ? dev->transport->get_blocks(dev) > U32_MAX : false; +} diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 5a9982f5d5d6..0f19e11acac2 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -105,6 +105,8 @@ static int iblock_configure_device(struct se_device *dev) mode = FMODE_READ|FMODE_EXCL; if (!ib_dev->ibd_readonly) mode |= FMODE_WRITE; + else + dev->dev_flags |= DF_READ_ONLY; bd = blkdev_get_by_path(ib_dev->ibd_udev_path, mode, ib_dev); if (IS_ERR(bd)) { diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 5ab7100de17e..e7933115087a 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -618,7 +618,7 @@ static struct t10_pr_registration *__core_scsi3_do_alloc_registration( struct se_device *dev, struct se_node_acl *nacl, struct se_lun *lun, - struct se_dev_entry *deve, + struct se_dev_entry *dest_deve, u64 mapped_lun, unsigned char *isid, u64 sa_res_key, @@ -640,7 +640,29 @@ static struct t10_pr_registration *__core_scsi3_do_alloc_registration( INIT_LIST_HEAD(&pr_reg->pr_reg_atp_mem_list); atomic_set(&pr_reg->pr_res_holders, 0); pr_reg->pr_reg_nacl = nacl; - pr_reg->pr_reg_deve = deve; + /* + * For destination registrations for ALL_TG_PT=1 and SPEC_I_PT=1, + * the se_dev_entry->pr_ref will have been already obtained by + * core_get_se_deve_from_rtpi() or __core_scsi3_alloc_registration(). + * + * Otherwise, locate se_dev_entry now and obtain a reference until + * registration completes in __core_scsi3_add_registration(). + */ + if (dest_deve) { + pr_reg->pr_reg_deve = dest_deve; + } else { + rcu_read_lock(); + pr_reg->pr_reg_deve = target_nacl_find_deve(nacl, mapped_lun); + if (!pr_reg->pr_reg_deve) { + rcu_read_unlock(); + pr_err("Unable to locate PR deve %s mapped_lun: %llu\n", + nacl->initiatorname, mapped_lun); + kmem_cache_free(t10_pr_reg_cache, pr_reg); + return NULL; + } + kref_get(&pr_reg->pr_reg_deve->pr_kref); + rcu_read_unlock(); + } pr_reg->pr_res_mapped_lun = mapped_lun; pr_reg->pr_aptpl_target_lun = lun->unpacked_lun; pr_reg->tg_pt_sep_rtpi = lun->lun_rtpi; @@ -936,17 +958,29 @@ static int __core_scsi3_check_aptpl_registration( !(strcmp(pr_reg->pr_tport, t_port)) && (pr_reg->pr_reg_tpgt == tpgt) && (pr_reg->pr_aptpl_target_lun == target_lun)) { + /* + * Obtain the ->pr_reg_deve pointer + reference, that + * is released by __core_scsi3_add_registration() below. + */ + rcu_read_lock(); + pr_reg->pr_reg_deve = target_nacl_find_deve(nacl, mapped_lun); + if (!pr_reg->pr_reg_deve) { + pr_err("Unable to locate PR APTPL %s mapped_lun:" + " %llu\n", nacl->initiatorname, mapped_lun); + rcu_read_unlock(); + continue; + } + kref_get(&pr_reg->pr_reg_deve->pr_kref); + rcu_read_unlock(); pr_reg->pr_reg_nacl = nacl; pr_reg->tg_pt_sep_rtpi = lun->lun_rtpi; - list_del(&pr_reg->pr_reg_aptpl_list); spin_unlock(&pr_tmpl->aptpl_reg_lock); /* * At this point all of the pointers in *pr_reg will * be setup, so go ahead and add the registration. */ - __core_scsi3_add_registration(dev, nacl, pr_reg, 0, 0); /* * If this registration is the reservation holder, @@ -1044,18 +1078,11 @@ static void __core_scsi3_add_registration( __core_scsi3_dump_registration(tfo, dev, nacl, pr_reg, register_type); spin_unlock(&pr_tmpl->registration_lock); - - rcu_read_lock(); - deve = pr_reg->pr_reg_deve; - if (deve) - set_bit(DEF_PR_REG_ACTIVE, &deve->deve_flags); - rcu_read_unlock(); - /* * Skip extra processing for ALL_TG_PT=0 or REGISTER_AND_MOVE. */ if (!pr_reg->pr_reg_all_tg_pt || register_move) - return; + goto out; /* * Walk pr_reg->pr_reg_atp_list and add registrations for ALL_TG_PT=1 * allocated in __core_scsi3_alloc_registration() @@ -1075,19 +1102,31 @@ static void __core_scsi3_add_registration( __core_scsi3_dump_registration(tfo, dev, nacl_tmp, pr_reg_tmp, register_type); spin_unlock(&pr_tmpl->registration_lock); - + /* + * Drop configfs group dependency reference and deve->pr_kref + * obtained from __core_scsi3_alloc_registration() code. + */ rcu_read_lock(); deve = pr_reg_tmp->pr_reg_deve; - if (deve) + if (deve) { set_bit(DEF_PR_REG_ACTIVE, &deve->deve_flags); + core_scsi3_lunacl_undepend_item(deve); + pr_reg_tmp->pr_reg_deve = NULL; + } rcu_read_unlock(); - - /* - * Drop configfs group dependency reference from - * __core_scsi3_alloc_registration() - */ - core_scsi3_lunacl_undepend_item(pr_reg_tmp->pr_reg_deve); } +out: + /* + * Drop deve->pr_kref obtained in __core_scsi3_do_alloc_registration() + */ + rcu_read_lock(); + deve = pr_reg->pr_reg_deve; + if (deve) { + set_bit(DEF_PR_REG_ACTIVE, &deve->deve_flags); + kref_put(&deve->pr_kref, target_pr_kref_release); + pr_reg->pr_reg_deve = NULL; + } + rcu_read_unlock(); } static int core_scsi3_alloc_registration( @@ -1785,9 +1824,11 @@ core_scsi3_decode_spec_i_port( dest_node_acl->initiatorname, i_buf, (dest_se_deve) ? dest_se_deve->mapped_lun : 0); - if (!dest_se_deve) + if (!dest_se_deve) { + kref_put(&local_pr_reg->pr_reg_deve->pr_kref, + target_pr_kref_release); continue; - + } core_scsi3_lunacl_undepend_item(dest_se_deve); core_scsi3_nodeacl_undepend_item(dest_node_acl); core_scsi3_tpg_undepend_item(dest_tpg); @@ -1823,9 +1864,11 @@ out: kmem_cache_free(t10_pr_reg_cache, dest_pr_reg); - if (!dest_se_deve) + if (!dest_se_deve) { + kref_put(&local_pr_reg->pr_reg_deve->pr_kref, + target_pr_kref_release); continue; - + } core_scsi3_lunacl_undepend_item(dest_se_deve); core_scsi3_nodeacl_undepend_item(dest_node_acl); core_scsi3_tpg_undepend_item(dest_tpg); diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index e318ddbe15da..0b4b2a67d9f9 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -154,6 +154,38 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd) return 0; } +static sense_reason_t +sbc_emulate_startstop(struct se_cmd *cmd) +{ + unsigned char *cdb = cmd->t_task_cdb; + + /* + * See sbc3r36 section 5.25 + * Immediate bit should be set since there is nothing to complete + * POWER CONDITION MODIFIER 0h + */ + if (!(cdb[1] & 1) || cdb[2] || cdb[3]) + return TCM_INVALID_CDB_FIELD; + + /* + * See sbc3r36 section 5.25 + * POWER CONDITION 0h START_VALID - process START and LOEJ + */ + if (cdb[4] >> 4 & 0xf) + return TCM_INVALID_CDB_FIELD; + + /* + * See sbc3r36 section 5.25 + * LOEJ 0h - nothing to load or unload + * START 1h - we are ready + */ + if (!(cdb[4] & 1) || (cdb[4] & 2) || (cdb[4] & 4)) + return TCM_INVALID_CDB_FIELD; + + target_complete_cmd(cmd, SAM_STAT_GOOD); + return 0; +} + sector_t sbc_get_write_same_sectors(struct se_cmd *cmd) { u32 num_blocks; @@ -960,6 +992,9 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) " than 1\n", sectors); return TCM_INVALID_CDB_FIELD; } + if (sbc_check_dpofua(dev, cmd, cdb)) + return TCM_INVALID_CDB_FIELD; + /* * Double size because we have two buffers, note that * zero is not an error.. @@ -1069,6 +1104,10 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) size = 0; cmd->execute_cmd = sbc_emulate_noop; break; + case START_STOP: + size = 0; + cmd->execute_cmd = sbc_emulate_startstop; + break; default: ret = spc_parse_cdb(cmd, &size); if (ret) @@ -1191,7 +1230,7 @@ void sbc_dif_generate(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; - struct se_dif_v1_tuple *sdt; + struct t10_pi_tuple *sdt; struct scatterlist *dsg = cmd->t_data_sg, *psg; sector_t sector = cmd->t_task_lba; void *daddr, *paddr; @@ -1203,7 +1242,7 @@ sbc_dif_generate(struct se_cmd *cmd) daddr = kmap_atomic(sg_page(dsg)) + dsg->offset; for (j = 0; j < psg->length; - j += sizeof(struct se_dif_v1_tuple)) { + j += sizeof(*sdt)) { __u16 crc; unsigned int avail; @@ -1256,7 +1295,7 @@ sbc_dif_generate(struct se_cmd *cmd) } static sense_reason_t -sbc_dif_v1_verify(struct se_cmd *cmd, struct se_dif_v1_tuple *sdt, +sbc_dif_v1_verify(struct se_cmd *cmd, struct t10_pi_tuple *sdt, __u16 crc, sector_t sector, unsigned int ei_lba) { __be16 csum; @@ -1346,7 +1385,7 @@ sbc_dif_verify(struct se_cmd *cmd, sector_t start, unsigned int sectors, unsigned int ei_lba, struct scatterlist *psg, int psg_off) { struct se_device *dev = cmd->se_dev; - struct se_dif_v1_tuple *sdt; + struct t10_pi_tuple *sdt; struct scatterlist *dsg = cmd->t_data_sg; sector_t sector = start; void *daddr, *paddr; @@ -1361,7 +1400,7 @@ sbc_dif_verify(struct se_cmd *cmd, sector_t start, unsigned int sectors, for (i = psg_off; i < psg->length && sector < start + sectors; - i += sizeof(struct se_dif_v1_tuple)) { + i += sizeof(*sdt)) { __u16 crc; unsigned int avail; diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index f87d4cef6d39..9413e1a949e5 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -484,8 +484,8 @@ static sense_reason_t spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) { struct se_device *dev = cmd->se_dev; - int have_tp = 0; - int opt, min; + u32 mtl = 0; + int have_tp = 0, opt, min; /* * Following spc3r22 section 6.5.3 Block Limits VPD page, when @@ -516,8 +516,15 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) /* * Set MAXIMUM TRANSFER LENGTH + * + * XXX: Currently assumes single PAGE_SIZE per scatterlist for fabrics + * enforcing maximum HW scatter-gather-list entry limit */ - put_unaligned_be32(dev->dev_attrib.hw_max_sectors, &buf[8]); + if (cmd->se_tfo->max_data_sg_nents) { + mtl = (cmd->se_tfo->max_data_sg_nents * PAGE_SIZE) / + dev->dev_attrib.block_size; + } + put_unaligned_be32(min_not_zero(mtl, dev->dev_attrib.hw_max_sectors), &buf[8]); /* * Set OPTIMAL TRANSFER LENGTH @@ -768,7 +775,12 @@ static int spc_modesense_control(struct se_cmd *cmd, u8 pc, u8 *p) if (pc == 1) goto out; - p[2] = 2; + /* GLTSD: No implicit save of log parameters */ + p[2] = (1 << 1); + if (target_sense_desc_format(dev)) + /* D_SENSE: Descriptor format sense data for 64bit sectors */ + p[2] |= (1 << 2); + /* * From spc4r23, 7.4.7 Control mode page * @@ -1151,6 +1163,7 @@ static sense_reason_t spc_emulate_request_sense(struct se_cmd *cmd) unsigned char *rbuf; u8 ua_asc = 0, ua_ascq = 0; unsigned char buf[SE_SENSE_BUF]; + bool desc_format = target_sense_desc_format(cmd->se_dev); memset(buf, 0, SE_SENSE_BUF); @@ -1164,32 +1177,11 @@ static sense_reason_t spc_emulate_request_sense(struct se_cmd *cmd) if (!rbuf) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) { - /* - * CURRENT ERROR, UNIT ATTENTION - */ - buf[0] = 0x70; - buf[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION; - - /* - * The Additional Sense Code (ASC) from the UNIT ATTENTION - */ - buf[SPC_ASC_KEY_OFFSET] = ua_asc; - buf[SPC_ASCQ_KEY_OFFSET] = ua_ascq; - buf[7] = 0x0A; - } else { - /* - * CURRENT ERROR, NO SENSE - */ - buf[0] = 0x70; - buf[SPC_SENSE_KEY_OFFSET] = NO_SENSE; - - /* - * NO ADDITIONAL SENSE INFORMATION - */ - buf[SPC_ASC_KEY_OFFSET] = 0x00; - buf[7] = 0x0A; - } + if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) + scsi_build_sense_buffer(desc_format, buf, UNIT_ATTENTION, + ua_asc, ua_ascq); + else + scsi_build_sense_buffer(desc_format, buf, NO_SENSE, 0x0, 0x0); memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); transport_kunmap_data_sg(cmd); @@ -1418,9 +1410,6 @@ spc_parse_cdb(struct se_cmd *cmd, unsigned int *size) } break; default: - pr_warn("TARGET_CORE[%s]: Unsupported SCSI Opcode" - " 0x%02x, sending CHECK_CONDITION.\n", - cmd->se_tfo->get_fabric_name(), cdb[0]); return TCM_UNSUPPORTED_SCSI_OPCODE; } diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index babde4ad841f..5fb9dd7f08bb 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -41,6 +41,7 @@ #include "target_core_internal.h" #include "target_core_alua.h" #include "target_core_pr.h" +#include "target_core_ua.h" extern struct se_device *g_lun0_dev; @@ -83,6 +84,22 @@ struct se_node_acl *core_tpg_get_initiator_node_acl( } EXPORT_SYMBOL(core_tpg_get_initiator_node_acl); +void core_allocate_nexus_loss_ua( + struct se_node_acl *nacl) +{ + struct se_dev_entry *deve; + + if (!nacl) + return; + + rcu_read_lock(); + hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) + core_scsi3_ua_allocate(deve, 0x29, + ASCQ_29H_NEXUS_LOSS_OCCURRED); + rcu_read_unlock(); +} +EXPORT_SYMBOL(core_allocate_nexus_loss_ua); + /* core_tpg_add_node_to_devs(): * * @@ -651,7 +668,10 @@ int core_tpg_add_lun( list_add_tail(&lun->lun_dev_link, &dev->dev_sep_list); spin_unlock(&dev->se_port_lock); - lun->lun_access = lun_access; + if (dev->dev_flags & DF_READ_ONLY) + lun->lun_access = TRANSPORT_LUNFLAGS_READ_ONLY; + else + lun->lun_access = lun_access; if (!(dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)) hlist_add_head_rcu(&lun->link, &tpg->tpg_lun_hlist); mutex_unlock(&tpg->tpg_lun_mutex); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index ce8574b7220c..5bacc7b5ed6d 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -39,6 +39,7 @@ #include <net/sock.h> #include <net/tcp.h> #include <scsi/scsi_proto.h> +#include <scsi/scsi_common.h> #include <target/target_core_base.h> #include <target/target_core_backend.h> @@ -1074,6 +1075,55 @@ transport_set_vpd_ident(struct t10_vpd *vpd, unsigned char *page_83) } EXPORT_SYMBOL(transport_set_vpd_ident); +static sense_reason_t +target_check_max_data_sg_nents(struct se_cmd *cmd, struct se_device *dev, + unsigned int size) +{ + u32 mtl; + + if (!cmd->se_tfo->max_data_sg_nents) + return TCM_NO_SENSE; + /* + * Check if fabric enforced maximum SGL entries per I/O descriptor + * exceeds se_cmd->data_length. If true, set SCF_UNDERFLOW_BIT + + * residual_count and reduce original cmd->data_length to maximum + * length based on single PAGE_SIZE entry scatter-lists. + */ + mtl = (cmd->se_tfo->max_data_sg_nents * PAGE_SIZE); + if (cmd->data_length > mtl) { + /* + * If an existing CDB overflow is present, calculate new residual + * based on CDB size minus fabric maximum transfer length. + * + * If an existing CDB underflow is present, calculate new residual + * based on original cmd->data_length minus fabric maximum transfer + * length. + * + * Otherwise, set the underflow residual based on cmd->data_length + * minus fabric maximum transfer length. + */ + if (cmd->se_cmd_flags & SCF_OVERFLOW_BIT) { + cmd->residual_count = (size - mtl); + } else if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) { + u32 orig_dl = size + cmd->residual_count; + cmd->residual_count = (orig_dl - mtl); + } else { + cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT; + cmd->residual_count = (cmd->data_length - mtl); + } + cmd->data_length = mtl; + /* + * Reset sbc_check_prot() calculated protection payload + * length based upon the new smaller MTL. + */ + if (cmd->prot_length) { + u32 sectors = (mtl / dev->dev_attrib.block_size); + cmd->prot_length = dev->prot_length * sectors; + } + } + return TCM_NO_SENSE; +} + sense_reason_t target_cmd_size_check(struct se_cmd *cmd, unsigned int size) { @@ -1087,9 +1137,9 @@ target_cmd_size_check(struct se_cmd *cmd, unsigned int size) " 0x%02x\n", cmd->se_tfo->get_fabric_name(), cmd->data_length, size, cmd->t_task_cdb[0]); - if (cmd->data_direction == DMA_TO_DEVICE) { - pr_err("Rejecting underflow/overflow" - " WRITE data\n"); + if (cmd->data_direction == DMA_TO_DEVICE && + cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) { + pr_err("Rejecting underflow/overflow WRITE data\n"); return TCM_INVALID_CDB_FIELD; } /* @@ -1119,7 +1169,7 @@ target_cmd_size_check(struct se_cmd *cmd, unsigned int size) } } - return 0; + return target_check_max_data_sg_nents(cmd, dev, size); } @@ -1177,14 +1227,7 @@ transport_check_alloc_task_attr(struct se_cmd *cmd) " emulation is not supported\n"); return TCM_INVALID_CDB_FIELD; } - /* - * Used to determine when ORDERED commands should go from - * Dormant to Active status. - */ - cmd->se_ordered_id = atomic_inc_return(&dev->dev_ordered_id); - pr_debug("Allocated se_ordered_id: %u for Task Attr: 0x%02x on %s\n", - cmd->se_ordered_id, cmd->sam_task_attr, - dev->transport->name); + return 0; } @@ -1246,6 +1289,11 @@ target_setup_cmd_from_cdb(struct se_cmd *cmd, unsigned char *cdb) } ret = dev->transport->parse_cdb(cmd); + if (ret == TCM_UNSUPPORTED_SCSI_OPCODE) + pr_warn_ratelimited("%s/%s: Unsupported SCSI Opcode 0x%02x, sending CHECK_CONDITION.\n", + cmd->se_tfo->get_fabric_name(), + cmd->se_sess->se_node_acl->initiatorname, + cmd->t_task_cdb[0]); if (ret) return ret; @@ -1693,8 +1741,7 @@ void transport_generic_request_failure(struct se_cmd *cmd, check_stop: transport_lun_remove_cmd(cmd); - if (!transport_cmd_check_stop_to_fabric(cmd)) - ; + transport_cmd_check_stop_to_fabric(cmd); return; queue_full: @@ -1767,16 +1814,14 @@ static bool target_handle_task_attr(struct se_cmd *cmd) */ switch (cmd->sam_task_attr) { case TCM_HEAD_TAG: - pr_debug("Added HEAD_OF_QUEUE for CDB: 0x%02x, " - "se_ordered_id: %u\n", - cmd->t_task_cdb[0], cmd->se_ordered_id); + pr_debug("Added HEAD_OF_QUEUE for CDB: 0x%02x\n", + cmd->t_task_cdb[0]); return false; case TCM_ORDERED_TAG: atomic_inc_mb(&dev->dev_ordered_sync); - pr_debug("Added ORDERED for CDB: 0x%02x to ordered list, " - " se_ordered_id: %u\n", - cmd->t_task_cdb[0], cmd->se_ordered_id); + pr_debug("Added ORDERED for CDB: 0x%02x to ordered list\n", + cmd->t_task_cdb[0]); /* * Execute an ORDERED command if no other older commands @@ -1800,10 +1845,8 @@ static bool target_handle_task_attr(struct se_cmd *cmd) list_add_tail(&cmd->se_delayed_node, &dev->delayed_cmd_list); spin_unlock(&dev->delayed_cmd_lock); - pr_debug("Added CDB: 0x%02x Task Attr: 0x%02x to" - " delayed CMD list, se_ordered_id: %u\n", - cmd->t_task_cdb[0], cmd->sam_task_attr, - cmd->se_ordered_id); + pr_debug("Added CDB: 0x%02x Task Attr: 0x%02x to delayed CMD listn", + cmd->t_task_cdb[0], cmd->sam_task_attr); return true; } @@ -1888,20 +1931,18 @@ static void transport_complete_task_attr(struct se_cmd *cmd) if (cmd->sam_task_attr == TCM_SIMPLE_TAG) { atomic_dec_mb(&dev->simple_cmds); dev->dev_cur_ordered_id++; - pr_debug("Incremented dev->dev_cur_ordered_id: %u for" - " SIMPLE: %u\n", dev->dev_cur_ordered_id, - cmd->se_ordered_id); + pr_debug("Incremented dev->dev_cur_ordered_id: %u for SIMPLE\n", + dev->dev_cur_ordered_id); } else if (cmd->sam_task_attr == TCM_HEAD_TAG) { dev->dev_cur_ordered_id++; - pr_debug("Incremented dev_cur_ordered_id: %u for" - " HEAD_OF_QUEUE: %u\n", dev->dev_cur_ordered_id, - cmd->se_ordered_id); + pr_debug("Incremented dev_cur_ordered_id: %u for HEAD_OF_QUEUE\n", + dev->dev_cur_ordered_id); } else if (cmd->sam_task_attr == TCM_ORDERED_TAG) { atomic_dec_mb(&dev->dev_ordered_sync); dev->dev_cur_ordered_id++; - pr_debug("Incremented dev_cur_ordered_id: %u for ORDERED:" - " %u\n", dev->dev_cur_ordered_id, cmd->se_ordered_id); + pr_debug("Incremented dev_cur_ordered_id: %u for ORDERED\n", + dev->dev_cur_ordered_id); } target_restart_delayed_cmds(dev); @@ -2615,37 +2656,159 @@ bool transport_wait_for_tasks(struct se_cmd *cmd) } EXPORT_SYMBOL(transport_wait_for_tasks); -static int transport_get_sense_codes( - struct se_cmd *cmd, - u8 *asc, - u8 *ascq) +struct sense_info { + u8 key; + u8 asc; + u8 ascq; + bool add_sector_info; +}; + +static const struct sense_info sense_info_table[] = { + [TCM_NO_SENSE] = { + .key = NOT_READY + }, + [TCM_NON_EXISTENT_LUN] = { + .key = ILLEGAL_REQUEST, + .asc = 0x25 /* LOGICAL UNIT NOT SUPPORTED */ + }, + [TCM_UNSUPPORTED_SCSI_OPCODE] = { + .key = ILLEGAL_REQUEST, + .asc = 0x20, /* INVALID COMMAND OPERATION CODE */ + }, + [TCM_SECTOR_COUNT_TOO_MANY] = { + .key = ILLEGAL_REQUEST, + .asc = 0x20, /* INVALID COMMAND OPERATION CODE */ + }, + [TCM_UNKNOWN_MODE_PAGE] = { + .key = ILLEGAL_REQUEST, + .asc = 0x24, /* INVALID FIELD IN CDB */ + }, + [TCM_CHECK_CONDITION_ABORT_CMD] = { + .key = ABORTED_COMMAND, + .asc = 0x29, /* BUS DEVICE RESET FUNCTION OCCURRED */ + .ascq = 0x03, + }, + [TCM_INCORRECT_AMOUNT_OF_DATA] = { + .key = ABORTED_COMMAND, + .asc = 0x0c, /* WRITE ERROR */ + .ascq = 0x0d, /* NOT ENOUGH UNSOLICITED DATA */ + }, + [TCM_INVALID_CDB_FIELD] = { + .key = ILLEGAL_REQUEST, + .asc = 0x24, /* INVALID FIELD IN CDB */ + }, + [TCM_INVALID_PARAMETER_LIST] = { + .key = ILLEGAL_REQUEST, + .asc = 0x26, /* INVALID FIELD IN PARAMETER LIST */ + }, + [TCM_PARAMETER_LIST_LENGTH_ERROR] = { + .key = ILLEGAL_REQUEST, + .asc = 0x1a, /* PARAMETER LIST LENGTH ERROR */ + }, + [TCM_UNEXPECTED_UNSOLICITED_DATA] = { + .key = ILLEGAL_REQUEST, + .asc = 0x0c, /* WRITE ERROR */ + .ascq = 0x0c, /* UNEXPECTED_UNSOLICITED_DATA */ + }, + [TCM_SERVICE_CRC_ERROR] = { + .key = ABORTED_COMMAND, + .asc = 0x47, /* PROTOCOL SERVICE CRC ERROR */ + .ascq = 0x05, /* N/A */ + }, + [TCM_SNACK_REJECTED] = { + .key = ABORTED_COMMAND, + .asc = 0x11, /* READ ERROR */ + .ascq = 0x13, /* FAILED RETRANSMISSION REQUEST */ + }, + [TCM_WRITE_PROTECTED] = { + .key = DATA_PROTECT, + .asc = 0x27, /* WRITE PROTECTED */ + }, + [TCM_ADDRESS_OUT_OF_RANGE] = { + .key = ILLEGAL_REQUEST, + .asc = 0x21, /* LOGICAL BLOCK ADDRESS OUT OF RANGE */ + }, + [TCM_CHECK_CONDITION_UNIT_ATTENTION] = { + .key = UNIT_ATTENTION, + }, + [TCM_CHECK_CONDITION_NOT_READY] = { + .key = NOT_READY, + }, + [TCM_MISCOMPARE_VERIFY] = { + .key = MISCOMPARE, + .asc = 0x1d, /* MISCOMPARE DURING VERIFY OPERATION */ + .ascq = 0x00, + }, + [TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED] = { + .key = ABORTED_COMMAND, + .asc = 0x10, + .ascq = 0x01, /* LOGICAL BLOCK GUARD CHECK FAILED */ + .add_sector_info = true, + }, + [TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED] = { + .key = ABORTED_COMMAND, + .asc = 0x10, + .ascq = 0x02, /* LOGICAL BLOCK APPLICATION TAG CHECK FAILED */ + .add_sector_info = true, + }, + [TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED] = { + .key = ABORTED_COMMAND, + .asc = 0x10, + .ascq = 0x03, /* LOGICAL BLOCK REFERENCE TAG CHECK FAILED */ + .add_sector_info = true, + }, + [TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE] = { + /* + * Returning ILLEGAL REQUEST would cause immediate IO errors on + * Solaris initiators. Returning NOT READY instead means the + * operations will be retried a finite number of times and we + * can survive intermittent errors. + */ + .key = NOT_READY, + .asc = 0x08, /* LOGICAL UNIT COMMUNICATION FAILURE */ + }, +}; + +static int translate_sense_reason(struct se_cmd *cmd, sense_reason_t reason) { - *asc = cmd->scsi_asc; - *ascq = cmd->scsi_ascq; + const struct sense_info *si; + u8 *buffer = cmd->sense_buffer; + int r = (__force int)reason; + u8 asc, ascq; + bool desc_format = target_sense_desc_format(cmd->se_dev); - return 0; -} + if (r < ARRAY_SIZE(sense_info_table) && sense_info_table[r].key) + si = &sense_info_table[r]; + else + si = &sense_info_table[(__force int) + TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE]; -static -void transport_err_sector_info(unsigned char *buffer, sector_t bad_sector) -{ - /* Place failed LBA in sense data information descriptor 0. */ - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 0xc; - buffer[SPC_DESC_TYPE_OFFSET] = 0; /* Information */ - buffer[SPC_ADDITIONAL_DESC_LEN_OFFSET] = 0xa; - buffer[SPC_VALIDITY_OFFSET] = 0x80; + if (reason == TCM_CHECK_CONDITION_UNIT_ATTENTION) { + core_scsi3_ua_for_check_condition(cmd, &asc, &ascq); + WARN_ON_ONCE(asc == 0); + } else if (si->asc == 0) { + WARN_ON_ONCE(cmd->scsi_asc == 0); + asc = cmd->scsi_asc; + ascq = cmd->scsi_ascq; + } else { + asc = si->asc; + ascq = si->ascq; + } + + scsi_build_sense_buffer(desc_format, buffer, si->key, asc, ascq); + if (si->add_sector_info) + return scsi_set_sense_information(buffer, + cmd->scsi_sense_length, + cmd->bad_sector); - /* Descriptor Information: failing sector */ - put_unaligned_be64(bad_sector, &buffer[12]); + return 0; } int transport_send_check_condition_and_sense(struct se_cmd *cmd, sense_reason_t reason, int from_transport) { - unsigned char *buffer = cmd->sense_buffer; unsigned long flags; - u8 asc = 0, ascq = 0; spin_lock_irqsave(&cmd->t_state_lock, flags); if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) { @@ -2655,243 +2818,17 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, cmd->se_cmd_flags |= SCF_SENT_CHECK_CONDITION; spin_unlock_irqrestore(&cmd->t_state_lock, flags); - if (!reason && from_transport) - goto after_reason; + if (!from_transport) { + int rc; - if (!from_transport) cmd->se_cmd_flags |= SCF_EMULATED_TASK_SENSE; - - /* - * Actual SENSE DATA, see SPC-3 7.23.2 SPC_SENSE_KEY_OFFSET uses - * SENSE KEY values from include/scsi/scsi.h - */ - switch (reason) { - case TCM_NO_SENSE: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* Not Ready */ - buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY; - /* NO ADDITIONAL SENSE INFORMATION */ - buffer[SPC_ASC_KEY_OFFSET] = 0; - buffer[SPC_ASCQ_KEY_OFFSET] = 0; - break; - case TCM_NON_EXISTENT_LUN: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; - /* LOGICAL UNIT NOT SUPPORTED */ - buffer[SPC_ASC_KEY_OFFSET] = 0x25; - break; - case TCM_UNSUPPORTED_SCSI_OPCODE: - case TCM_SECTOR_COUNT_TOO_MANY: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; - /* INVALID COMMAND OPERATION CODE */ - buffer[SPC_ASC_KEY_OFFSET] = 0x20; - break; - case TCM_UNKNOWN_MODE_PAGE: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; - /* INVALID FIELD IN CDB */ - buffer[SPC_ASC_KEY_OFFSET] = 0x24; - break; - case TCM_CHECK_CONDITION_ABORT_CMD: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ABORTED COMMAND */ - buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; - /* BUS DEVICE RESET FUNCTION OCCURRED */ - buffer[SPC_ASC_KEY_OFFSET] = 0x29; - buffer[SPC_ASCQ_KEY_OFFSET] = 0x03; - break; - case TCM_INCORRECT_AMOUNT_OF_DATA: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ABORTED COMMAND */ - buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; - /* WRITE ERROR */ - buffer[SPC_ASC_KEY_OFFSET] = 0x0c; - /* NOT ENOUGH UNSOLICITED DATA */ - buffer[SPC_ASCQ_KEY_OFFSET] = 0x0d; - break; - case TCM_INVALID_CDB_FIELD: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; - /* INVALID FIELD IN CDB */ - buffer[SPC_ASC_KEY_OFFSET] = 0x24; - break; - case TCM_INVALID_PARAMETER_LIST: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; - /* INVALID FIELD IN PARAMETER LIST */ - buffer[SPC_ASC_KEY_OFFSET] = 0x26; - break; - case TCM_PARAMETER_LIST_LENGTH_ERROR: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; - /* PARAMETER LIST LENGTH ERROR */ - buffer[SPC_ASC_KEY_OFFSET] = 0x1a; - break; - case TCM_UNEXPECTED_UNSOLICITED_DATA: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ABORTED COMMAND */ - buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; - /* WRITE ERROR */ - buffer[SPC_ASC_KEY_OFFSET] = 0x0c; - /* UNEXPECTED_UNSOLICITED_DATA */ - buffer[SPC_ASCQ_KEY_OFFSET] = 0x0c; - break; - case TCM_SERVICE_CRC_ERROR: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ABORTED COMMAND */ - buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; - /* PROTOCOL SERVICE CRC ERROR */ - buffer[SPC_ASC_KEY_OFFSET] = 0x47; - /* N/A */ - buffer[SPC_ASCQ_KEY_OFFSET] = 0x05; - break; - case TCM_SNACK_REJECTED: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ABORTED COMMAND */ - buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; - /* READ ERROR */ - buffer[SPC_ASC_KEY_OFFSET] = 0x11; - /* FAILED RETRANSMISSION REQUEST */ - buffer[SPC_ASCQ_KEY_OFFSET] = 0x13; - break; - case TCM_WRITE_PROTECTED: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* DATA PROTECT */ - buffer[SPC_SENSE_KEY_OFFSET] = DATA_PROTECT; - /* WRITE PROTECTED */ - buffer[SPC_ASC_KEY_OFFSET] = 0x27; - break; - case TCM_ADDRESS_OUT_OF_RANGE: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; - /* LOGICAL BLOCK ADDRESS OUT OF RANGE */ - buffer[SPC_ASC_KEY_OFFSET] = 0x21; - break; - case TCM_CHECK_CONDITION_UNIT_ATTENTION: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* UNIT ATTENTION */ - buffer[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION; - core_scsi3_ua_for_check_condition(cmd, &asc, &ascq); - buffer[SPC_ASC_KEY_OFFSET] = asc; - buffer[SPC_ASCQ_KEY_OFFSET] = ascq; - break; - case TCM_CHECK_CONDITION_NOT_READY: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* Not Ready */ - buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY; - transport_get_sense_codes(cmd, &asc, &ascq); - buffer[SPC_ASC_KEY_OFFSET] = asc; - buffer[SPC_ASCQ_KEY_OFFSET] = ascq; - break; - case TCM_MISCOMPARE_VERIFY: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - buffer[SPC_SENSE_KEY_OFFSET] = MISCOMPARE; - /* MISCOMPARE DURING VERIFY OPERATION */ - buffer[SPC_ASC_KEY_OFFSET] = 0x1d; - buffer[SPC_ASCQ_KEY_OFFSET] = 0x00; - break; - case TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; - /* LOGICAL BLOCK GUARD CHECK FAILED */ - buffer[SPC_ASC_KEY_OFFSET] = 0x10; - buffer[SPC_ASCQ_KEY_OFFSET] = 0x01; - transport_err_sector_info(buffer, cmd->bad_sector); - break; - case TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; - /* LOGICAL BLOCK APPLICATION TAG CHECK FAILED */ - buffer[SPC_ASC_KEY_OFFSET] = 0x10; - buffer[SPC_ASCQ_KEY_OFFSET] = 0x02; - transport_err_sector_info(buffer, cmd->bad_sector); - break; - case TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ILLEGAL REQUEST */ - buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; - /* LOGICAL BLOCK REFERENCE TAG CHECK FAILED */ - buffer[SPC_ASC_KEY_OFFSET] = 0x10; - buffer[SPC_ASCQ_KEY_OFFSET] = 0x03; - transport_err_sector_info(buffer, cmd->bad_sector); - break; - case TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE: - default: - /* CURRENT ERROR */ - buffer[0] = 0x70; - buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* - * Returning ILLEGAL REQUEST would cause immediate IO errors on - * Solaris initiators. Returning NOT READY instead means the - * operations will be retried a finite number of times and we - * can survive intermittent errors. - */ - buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY; - /* LOGICAL UNIT COMMUNICATION FAILURE */ - buffer[SPC_ASC_KEY_OFFSET] = 0x08; - break; + cmd->scsi_status = SAM_STAT_CHECK_CONDITION; + cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER; + rc = translate_sense_reason(cmd, reason); + if (rc) + return rc; } - /* - * This code uses linux/include/scsi/scsi.h SAM status codes! - */ - cmd->scsi_status = SAM_STAT_CHECK_CONDITION; - /* - * Automatically padded, this value is encoded in the fabric's - * data_length response PDU containing the SCSI defined sense data. - */ - cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER; -after_reason: trace_target_cmd_complete(cmd); return cmd->se_tfo->queue_status(cmd); } diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index c448ef421ce7..937cebf76633 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -25,6 +25,7 @@ #include <linux/parser.h> #include <linux/vmalloc.h> #include <linux/uio_driver.h> +#include <linux/stringify.h> #include <net/genetlink.h> #include <scsi/scsi_common.h> #include <scsi/scsi_proto.h> @@ -538,14 +539,8 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); pr_warn("TCMU: Userspace set UNKNOWN_OP flag on se_cmd %p\n", cmd->se_cmd); - transport_generic_request_failure(cmd->se_cmd, - TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE); - cmd->se_cmd = NULL; - kmem_cache_free(tcmu_cmd_cache, cmd); - return; - } - - if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { + entry->rsp.scsi_status = SAM_STAT_CHECK_CONDITION; + } else if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { memcpy(se_cmd->sense_buffer, entry->rsp.sense_buffer, se_cmd->scsi_sense_length); @@ -577,7 +572,6 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * static unsigned int tcmu_handle_completions(struct tcmu_dev *udev) { struct tcmu_mailbox *mb; - LIST_HEAD(cpl_cmds); unsigned long flags; int handled = 0; @@ -905,7 +899,7 @@ static int tcmu_configure_device(struct se_device *dev) WARN_ON(!PAGE_ALIGNED(udev->data_off)); WARN_ON(udev->data_size % PAGE_SIZE); - info->version = xstr(TCMU_MAILBOX_VERSION); + info->version = __stringify(TCMU_MAILBOX_VERSION); info->mem[0].name = "tcm-user command & data buffer"; info->mem[0].addr = (phys_addr_t) udev->mb_addr; diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index 4515f52546f8..47fe94ee10b8 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c @@ -450,6 +450,8 @@ int target_xcopy_setup_pt(void) memset(&xcopy_pt_sess, 0, sizeof(struct se_session)); INIT_LIST_HEAD(&xcopy_pt_sess.sess_list); INIT_LIST_HEAD(&xcopy_pt_sess.sess_acl_list); + INIT_LIST_HEAD(&xcopy_pt_sess.sess_cmd_list); + spin_lock_init(&xcopy_pt_sess.sess_cmd_lock); xcopy_pt_nacl.se_tpg = &xcopy_pt_tpg; xcopy_pt_nacl.nacl_sess = &xcopy_pt_sess; @@ -644,7 +646,7 @@ static int target_xcopy_read_source( pr_debug("XCOPY: Built READ_16: LBA: %llu Sectors: %u Length: %u\n", (unsigned long long)src_lba, src_sectors, length); - transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length, + transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, length, DMA_FROM_DEVICE, 0, &xpt_cmd->sense_buffer[0]); xop->src_pt_cmd = xpt_cmd; @@ -704,7 +706,7 @@ static int target_xcopy_write_destination( pr_debug("XCOPY: Built WRITE_16: LBA: %llu Sectors: %u Length: %u\n", (unsigned long long)dst_lba, dst_sectors, length); - transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length, + transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, length, DMA_TO_DEVICE, 0, &xpt_cmd->sense_buffer[0]); xop->dst_pt_cmd = xpt_cmd; diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index 68031723e5be..aa3caca8bace 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -255,7 +255,7 @@ static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg) struct ft_cmd *cmd = arg; struct fc_frame_header *fh; - if (unlikely(IS_ERR(fp))) { + if (IS_ERR(fp)) { /* XXX need to find cmd if queued */ cmd->seq = NULL; cmd->aborted = true; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 118938ee8552..5aabc4bc0d75 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -163,7 +163,7 @@ config THERMAL_EMULATION config HISI_THERMAL tristate "Hisilicon thermal driver" - depends on ARCH_HISI && CPU_THERMAL && OF + depends on (ARCH_HISI && CPU_THERMAL && OF) || COMPILE_TEST help Enable this to plug hisilicon's thermal sensor driver into the Linux thermal framework. cpufreq is used as the cooling device to throttle @@ -182,7 +182,7 @@ config IMX_THERMAL config SPEAR_THERMAL bool "SPEAr thermal sensor driver" - depends on PLAT_SPEAR + depends on PLAT_SPEAR || COMPILE_TEST depends on OF help Enable this to plug the SPEAr thermal sensor driver into the Linux @@ -190,7 +190,7 @@ config SPEAR_THERMAL config ROCKCHIP_THERMAL tristate "Rockchip thermal driver" - depends on ARCH_ROCKCHIP + depends on ARCH_ROCKCHIP || COMPILE_TEST depends on RESET_CONTROLLER help Rockchip thermal driver provides support for Temperature sensor @@ -208,7 +208,7 @@ config RCAR_THERMAL config KIRKWOOD_THERMAL tristate "Temperature sensor on Marvell Kirkwood SoCs" - depends on MACH_KIRKWOOD + depends on MACH_KIRKWOOD || COMPILE_TEST depends on OF help Support for the Kirkwood thermal sensor driver into the Linux thermal @@ -216,7 +216,7 @@ config KIRKWOOD_THERMAL config DOVE_THERMAL tristate "Temperature sensor on Marvell Dove SoCs" - depends on ARCH_DOVE || MACH_DOVE + depends on ARCH_DOVE || MACH_DOVE || COMPILE_TEST depends on OF help Support for the Dove thermal sensor driver in the Linux thermal @@ -234,7 +234,7 @@ config DB8500_THERMAL config ARMADA_THERMAL tristate "Armada 370/XP thermal management" - depends on ARCH_MVEBU + depends on ARCH_MVEBU || COMPILE_TEST depends on OF help Enable this option if you want to have support for thermal management @@ -340,12 +340,21 @@ config ACPI_THERMAL_REL tristate depends on ACPI +config INTEL_PCH_THERMAL + tristate "Intel PCH Thermal Reporting Driver" + depends on X86 && PCI + help + Enable this to support thermal reporting on certain intel PCHs. + Thermal reporting device will provide temperature reading, + programmable trip points and other information. + menu "Texas Instruments thermal drivers" +depends on ARCH_HAS_BANDGAP || COMPILE_TEST source "drivers/thermal/ti-soc-thermal/Kconfig" endmenu menu "Samsung thermal drivers" -depends on ARCH_EXYNOS +depends on ARCH_EXYNOS || COMPILE_TEST source "drivers/thermal/samsung/Kconfig" endmenu @@ -356,7 +365,7 @@ endmenu config QCOM_SPMI_TEMP_ALARM tristate "Qualcomm SPMI PMIC Temperature Alarm" - depends on OF && SPMI && IIO + depends on OF && (SPMI || COMPILE_TEST) && IIO select REGMAP_SPMI help This enables a thermal sysfs driver for Qualcomm plug-and-play (QPNP) diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 535dfee1496f..26f160809959 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ +obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o obj-$(CONFIG_ST_THERMAL) += st/ obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 01255fd65135..26b8d326546a 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -155,7 +155,7 @@ static bool armada_is_valid(struct armada_thermal_priv *priv) } static int armada_get_temp(struct thermal_zone_device *thermal, - unsigned long *temp) + int *temp) { struct armada_thermal_priv *priv = thermal->devdata; unsigned long reg; diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 620dcd405ff6..42c6f71bdcc1 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -262,7 +262,9 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, * efficiently. Power is stored in mW, frequency in KHz. The * resulting table is in ascending order. * - * Return: 0 on success, -E* on error. + * Return: 0 on success, -EINVAL if there are no OPPs for any CPUs, + * -ENOMEM if we run out of memory or -EAGAIN if an OPP was + * added/enabled while the function was executing. */ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, u32 capacitance) @@ -273,8 +275,6 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, int num_opps = 0, cpu, i, ret = 0; unsigned long freq; - rcu_read_lock(); - for_each_cpu(cpu, &cpufreq_device->allowed_cpus) { dev = get_cpu_device(cpu); if (!dev) { @@ -284,24 +284,20 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, } num_opps = dev_pm_opp_get_opp_count(dev); - if (num_opps > 0) { + if (num_opps > 0) break; - } else if (num_opps < 0) { - ret = num_opps; - goto unlock; - } + else if (num_opps < 0) + return num_opps; } - if (num_opps == 0) { - ret = -EINVAL; - goto unlock; - } + if (num_opps == 0) + return -EINVAL; power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL); - if (!power_table) { - ret = -ENOMEM; - goto unlock; - } + if (!power_table) + return -ENOMEM; + + rcu_read_lock(); for (freq = 0, i = 0; opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp); @@ -309,6 +305,12 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, u32 freq_mhz, voltage_mv; u64 power; + if (i >= num_opps) { + rcu_read_unlock(); + ret = -EAGAIN; + goto free_power_table; + } + freq_mhz = freq / 1000000; voltage_mv = dev_pm_opp_get_voltage(opp) / 1000; @@ -326,17 +328,22 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, power_table[i].power = power; } - if (i == 0) { + rcu_read_unlock(); + + if (i != num_opps) { ret = PTR_ERR(opp); - goto unlock; + goto free_power_table; } cpufreq_device->cpu_dev = dev; cpufreq_device->dyn_power_table = power_table; cpufreq_device->dyn_power_table_entries = i; -unlock: - rcu_read_unlock(); + return 0; + +free_power_table: + kfree(power_table); + return ret; } @@ -847,7 +854,7 @@ __cpufreq_cooling_register(struct device_node *np, ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); if (ret) { cool_dev = ERR_PTR(ret); - goto free_table; + goto free_power_table; } snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", @@ -889,6 +896,8 @@ __cpufreq_cooling_register(struct device_node *np, remove_idr: release_idr(&cpufreq_idr, cpufreq_dev->id); +free_power_table: + kfree(cpufreq_dev->dyn_power_table); free_table: kfree(cpufreq_dev->freq_table); free_time_in_idle_timestamp: @@ -1039,6 +1048,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) thermal_cooling_device_unregister(cpufreq_dev->cool_dev); release_idr(&cpufreq_idr, cpufreq_dev->id); + kfree(cpufreq_dev->dyn_power_table); kfree(cpufreq_dev->time_in_idle_timestamp); kfree(cpufreq_dev->time_in_idle); kfree(cpufreq_dev->freq_table); diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c index 607b62c7e611..e58bd0b658b5 100644 --- a/drivers/thermal/db8500_cpufreq_cooling.c +++ b/drivers/thermal/db8500_cpufreq_cooling.c @@ -72,6 +72,7 @@ static const struct of_device_id db8500_cpufreq_cooling_match[] = { { .compatible = "stericsson,db8500-cpufreq-cooling" }, {}, }; +MODULE_DEVICE_TABLE(of, db8500_cpufreq_cooling_match); #endif static struct platform_driver db8500_cpufreq_cooling_driver = { diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c index 2fb273c4baa9..652acd8fbe48 100644 --- a/drivers/thermal/db8500_thermal.c +++ b/drivers/thermal/db8500_thermal.c @@ -107,8 +107,7 @@ static int db8500_cdev_unbind(struct thermal_zone_device *thermal, } /* Callback to get current temperature */ -static int db8500_sys_get_temp(struct thermal_zone_device *thermal, - unsigned long *temp) +static int db8500_sys_get_temp(struct thermal_zone_device *thermal, int *temp) { struct db8500_thermal_zone *pzone = thermal->devdata; @@ -180,7 +179,7 @@ static int db8500_sys_get_trip_type(struct thermal_zone_device *thermal, /* Callback to get trip point temperature */ static int db8500_sys_get_trip_temp(struct thermal_zone_device *thermal, - int trip, unsigned long *temp) + int trip, int *temp) { struct db8500_thermal_zone *pzone = thermal->devdata; struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; @@ -195,7 +194,7 @@ static int db8500_sys_get_trip_temp(struct thermal_zone_device *thermal, /* Callback to get critical trip point temperature */ static int db8500_sys_get_crit_temp(struct thermal_zone_device *thermal, - unsigned long *temp) + int *temp) { struct db8500_thermal_zone *pzone = thermal->devdata; struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c index 09f6e304c274..a0bc9de42553 100644 --- a/drivers/thermal/dove_thermal.c +++ b/drivers/thermal/dove_thermal.c @@ -93,7 +93,7 @@ static int dove_init_sensor(const struct dove_thermal_priv *priv) } static int dove_get_temp(struct thermal_zone_device *thermal, - unsigned long *temp) + int *temp) { unsigned long reg; struct dove_thermal_priv *priv = thermal->devdata; diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c index c2c10bbe24d6..34fe36504a55 100644 --- a/drivers/thermal/fair_share.c +++ b/drivers/thermal/fair_share.c @@ -34,7 +34,7 @@ static int get_trip_level(struct thermal_zone_device *tz) { int count = 0; - unsigned long trip_temp; + int trip_temp; enum thermal_trip_type trip_type; if (tz->trips == 0 || !tz->ops->get_trip_temp) diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index c5dd76b2ee74..70836c5b89bc 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -25,14 +25,13 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) { - long trip_temp; - unsigned long trip_hyst; + int trip_temp, trip_hyst; struct thermal_instance *instance; tz->ops->get_trip_temp(tz, trip, &trip_temp); tz->ops->get_trip_hyst(tz, trip, &trip_hyst); - dev_dbg(&tz->device, "Trip%d[temp=%ld]:temp=%d:hyst=%ld\n", + dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n", trip, trip_temp, tz->temperature, trip_hyst); diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index b49f97c734d0..36d07295f8e3 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -155,7 +155,7 @@ static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data) mutex_unlock(&data->thermal_lock); } -static int hisi_thermal_get_temp(void *_sensor, long *temp) +static int hisi_thermal_get_temp(void *_sensor, int *temp) { struct hisi_thermal_sensor *sensor = _sensor; struct hisi_thermal_data *data = sensor->thermal; @@ -178,7 +178,7 @@ static int hisi_thermal_get_temp(void *_sensor, long *temp) data->irq_bind_sensor = sensor_id; mutex_unlock(&data->thermal_lock); - dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%ld, thres=%d\n", + dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%d, thres=%d\n", sensor->id, data->irq_enabled, *temp, sensor->thres_temp); /* * Bind irq to sensor for two cases: diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index fde4c2876d14..4bec1d3c3d27 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -98,10 +98,10 @@ struct imx_thermal_data { enum thermal_device_mode mode; struct regmap *tempmon; u32 c1, c2; /* See formula in imx_get_sensor_data() */ - unsigned long temp_passive; - unsigned long temp_critical; - unsigned long alarm_temp; - unsigned long last_temp; + int temp_passive; + int temp_critical; + int alarm_temp; + int last_temp; bool irq_enabled; int irq; struct clk *thermal_clk; @@ -109,7 +109,7 @@ struct imx_thermal_data { }; static void imx_set_panic_temp(struct imx_thermal_data *data, - signed long panic_temp) + int panic_temp) { struct regmap *map = data->tempmon; int critical_value; @@ -121,7 +121,7 @@ static void imx_set_panic_temp(struct imx_thermal_data *data, } static void imx_set_alarm_temp(struct imx_thermal_data *data, - signed long alarm_temp) + int alarm_temp) { struct regmap *map = data->tempmon; int alarm_value; @@ -133,7 +133,7 @@ static void imx_set_alarm_temp(struct imx_thermal_data *data, TEMPSENSE0_ALARM_VALUE_SHIFT); } -static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) +static int imx_get_temp(struct thermal_zone_device *tz, int *temp) { struct imx_thermal_data *data = tz->devdata; struct regmap *map = data->tempmon; @@ -189,13 +189,13 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) if (data->alarm_temp == data->temp_critical && *temp < data->temp_passive) { imx_set_alarm_temp(data, data->temp_passive); - dev_dbg(&tz->device, "thermal alarm off: T < %lu\n", + dev_dbg(&tz->device, "thermal alarm off: T < %d\n", data->alarm_temp / 1000); } } if (*temp != data->last_temp) { - dev_dbg(&tz->device, "millicelsius: %ld\n", *temp); + dev_dbg(&tz->device, "millicelsius: %d\n", *temp); data->last_temp = *temp; } @@ -262,8 +262,7 @@ static int imx_get_trip_type(struct thermal_zone_device *tz, int trip, return 0; } -static int imx_get_crit_temp(struct thermal_zone_device *tz, - unsigned long *temp) +static int imx_get_crit_temp(struct thermal_zone_device *tz, int *temp) { struct imx_thermal_data *data = tz->devdata; @@ -272,7 +271,7 @@ static int imx_get_crit_temp(struct thermal_zone_device *tz, } static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip, - unsigned long *temp) + int *temp) { struct imx_thermal_data *data = tz->devdata; @@ -282,7 +281,7 @@ static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip, } static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip, - unsigned long temp) + int temp) { struct imx_thermal_data *data = tz->devdata; @@ -434,7 +433,7 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev) { struct imx_thermal_data *data = dev; - dev_dbg(&data->tz->device, "THERMAL ALARM: T > %lu\n", + dev_dbg(&data->tz->device, "THERMAL ALARM: T > %d\n", data->alarm_temp / 1000); thermal_zone_device_update(data->tz); diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c index 031018e7a65b..5836e5554433 100644 --- a/drivers/thermal/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c @@ -186,7 +186,7 @@ static int int3400_thermal_run_osc(acpi_handle handle, } static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, - unsigned long *temp) + int *temp) { *temp = 20 * 1000; /* faked temp sensor with 20C */ return 0; diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c index 1e25133d35e2..b9b2666aa94c 100644 --- a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c +++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c @@ -20,7 +20,7 @@ #include "int340x_thermal_zone.h" static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone, - unsigned long *temp) + int *temp) { struct int34x_thermal_zone *d = zone->devdata; unsigned long long tmp; @@ -49,7 +49,7 @@ static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone, } static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone, - int trip, unsigned long *temp) + int trip, int *temp) { struct int34x_thermal_zone *d = zone->devdata; int i; @@ -114,7 +114,7 @@ static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone, } static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone, - int trip, unsigned long temp) + int trip, int temp) { struct int34x_thermal_zone *d = zone->devdata; acpi_status status; @@ -136,7 +136,7 @@ static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone, static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone, - int trip, unsigned long *temp) + int trip, int *temp) { struct int34x_thermal_zone *d = zone->devdata; acpi_status status; @@ -163,7 +163,7 @@ static struct thermal_zone_device_ops int340x_thermal_zone_ops = { }; static int int340x_thermal_get_trip_config(acpi_handle handle, char *name, - unsigned long *temp) + int *temp) { unsigned long long r; acpi_status status; diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h index 9f38ab72c4bf..aaadf724ff2e 100644 --- a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h +++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h @@ -21,7 +21,7 @@ #define INT340X_THERMAL_MAX_ACT_TRIP_COUNT 10 struct active_trip { - unsigned long temp; + int temp; int id; bool valid; }; @@ -31,11 +31,11 @@ struct int34x_thermal_zone { struct active_trip act_trips[INT340X_THERMAL_MAX_ACT_TRIP_COUNT]; unsigned long *aux_trips; int aux_trip_nr; - unsigned long psv_temp; + int psv_temp; int psv_trip_id; - unsigned long crt_temp; + int crt_temp; int crt_trip_id; - unsigned long hot_temp; + int hot_temp; int hot_trip_id; struct thermal_zone_device *zone; struct thermal_zone_device_ops *override_ops; diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c index 3df3dc34b124..ccc0ad02d066 100644 --- a/drivers/thermal/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c @@ -145,7 +145,7 @@ static int get_tjmax(void) return -EINVAL; } -static int read_temp_msr(unsigned long *temp) +static int read_temp_msr(int *temp) { int cpu; u32 eax, edx; @@ -177,7 +177,7 @@ err_ret: } static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone, - unsigned long *temp) + int *temp) { int ret; diff --git a/drivers/thermal/intel_pch_thermal.c b/drivers/thermal/intel_pch_thermal.c new file mode 100644 index 000000000000..50c7da79be83 --- /dev/null +++ b/drivers/thermal/intel_pch_thermal.c @@ -0,0 +1,283 @@ +/* intel_pch_thermal.c - Intel PCH Thermal driver + * + * Copyright (c) 2015, Intel Corporation. + * + * Authors: + * Tushar Dave <tushar.n.dave@intel.com> + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/thermal.h> + +/* Intel PCH thermal Device IDs */ +#define PCH_THERMAL_DID_WPT 0x9CA4 /* Wildcat Point */ + +/* Wildcat Point-LP PCH Thermal registers */ +#define WPT_TEMP 0x0000 /* Temperature */ +#define WPT_TSC 0x04 /* Thermal Sensor Control */ +#define WPT_TSS 0x06 /* Thermal Sensor Status */ +#define WPT_TSEL 0x08 /* Thermal Sensor Enable and Lock */ +#define WPT_TSREL 0x0A /* Thermal Sensor Report Enable and Lock */ +#define WPT_TSMIC 0x0C /* Thermal Sensor SMI Control */ +#define WPT_CTT 0x0010 /* Catastrophic Trip Point */ +#define WPT_TAHV 0x0014 /* Thermal Alert High Value */ +#define WPT_TALV 0x0018 /* Thermal Alert Low Value */ +#define WPT_TL 0x00000040 /* Throttle Value */ +#define WPT_PHL 0x0060 /* PCH Hot Level */ +#define WPT_PHLC 0x62 /* PHL Control */ +#define WPT_TAS 0x80 /* Thermal Alert Status */ +#define WPT_TSPIEN 0x82 /* PCI Interrupt Event Enables */ +#define WPT_TSGPEN 0x84 /* General Purpose Event Enables */ + +/* Wildcat Point-LP PCH Thermal Register bit definitions */ +#define WPT_TEMP_TSR 0x00ff /* Temp TS Reading */ +#define WPT_TSC_CPDE 0x01 /* Catastrophic Power-Down Enable */ +#define WPT_TSS_TSDSS 0x10 /* Thermal Sensor Dynamic Shutdown Status */ +#define WPT_TSS_GPES 0x08 /* GPE status */ +#define WPT_TSEL_ETS 0x01 /* Enable TS */ +#define WPT_TSEL_PLDB 0x80 /* TSEL Policy Lock-Down Bit */ +#define WPT_TL_TOL 0x000001FF /* T0 Level */ +#define WPT_TL_T1L 0x1ff00000 /* T1 Level */ +#define WPT_TL_TTEN 0x20000000 /* TT Enable */ + +static char driver_name[] = "Intel PCH thermal driver"; + +struct pch_thermal_device { + void __iomem *hw_base; + const struct pch_dev_ops *ops; + struct pci_dev *pdev; + struct thermal_zone_device *tzd; + int crt_trip_id; + unsigned long crt_temp; + int hot_trip_id; + unsigned long hot_temp; +}; + +static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips) +{ + u8 tsel; + u16 trip_temp; + + *nr_trips = 0; + + /* Check if BIOS has already enabled thermal sensor */ + if (WPT_TSS_TSDSS & readb(ptd->hw_base + WPT_TSS)) + goto read_trips; + + tsel = readb(ptd->hw_base + WPT_TSEL); + /* + * When TSEL's Policy Lock-Down bit is 1, TSEL become RO. + * If so, thermal sensor cannot enable. Bail out. + */ + if (tsel & WPT_TSEL_PLDB) { + dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n"); + return -ENODEV; + } + + writeb(tsel|WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL); + if (!(WPT_TSS_TSDSS & readb(ptd->hw_base + WPT_TSS))) { + dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n"); + return -ENODEV; + } + +read_trips: + ptd->crt_trip_id = -1; + trip_temp = readw(ptd->hw_base + WPT_CTT); + trip_temp &= 0x1FF; + if (trip_temp) { + /* Resolution of 1/2 degree C and an offset of -50C */ + ptd->crt_temp = trip_temp * 1000 / 2 - 50000; + ptd->crt_trip_id = 0; + ++(*nr_trips); + } + + ptd->hot_trip_id = -1; + trip_temp = readw(ptd->hw_base + WPT_PHL); + trip_temp &= 0x1FF; + if (trip_temp) { + /* Resolution of 1/2 degree C and an offset of -50C */ + ptd->hot_temp = trip_temp * 1000 / 2 - 50000; + ptd->hot_trip_id = *nr_trips; + ++(*nr_trips); + } + + return 0; +} + +static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp) +{ + u8 wpt_temp; + + wpt_temp = WPT_TEMP_TSR & readl(ptd->hw_base + WPT_TEMP); + + /* Resolution of 1/2 degree C and an offset of -50C */ + *temp = (wpt_temp * 1000 / 2 - 50000); + + return 0; +} + +struct pch_dev_ops { + int (*hw_init)(struct pch_thermal_device *ptd, int *nr_trips); + int (*get_temp)(struct pch_thermal_device *ptd, int *temp); +}; + + +/* dev ops for Wildcat Point */ +static struct pch_dev_ops pch_dev_ops_wpt = { + .hw_init = pch_wpt_init, + .get_temp = pch_wpt_get_temp, +}; + +static int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp) +{ + struct pch_thermal_device *ptd = tzd->devdata; + + return ptd->ops->get_temp(ptd, temp); +} + +static int pch_get_trip_type(struct thermal_zone_device *tzd, int trip, + enum thermal_trip_type *type) +{ + struct pch_thermal_device *ptd = tzd->devdata; + + if (ptd->crt_trip_id == trip) + *type = THERMAL_TRIP_CRITICAL; + else if (ptd->hot_trip_id == trip) + *type = THERMAL_TRIP_HOT; + else + return -EINVAL; + + return 0; +} + +static int pch_get_trip_temp(struct thermal_zone_device *tzd, int trip, int *temp) +{ + struct pch_thermal_device *ptd = tzd->devdata; + + if (ptd->crt_trip_id == trip) + *temp = ptd->crt_temp; + else if (ptd->hot_trip_id == trip) + *temp = ptd->hot_temp; + else + return -EINVAL; + + return 0; +} + +static struct thermal_zone_device_ops tzd_ops = { + .get_temp = pch_thermal_get_temp, + .get_trip_type = pch_get_trip_type, + .get_trip_temp = pch_get_trip_temp, +}; + + +static int intel_pch_thermal_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pch_thermal_device *ptd; + int err; + int nr_trips; + char *dev_name; + + ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL); + if (!ptd) + return -ENOMEM; + + switch (pdev->device) { + case PCH_THERMAL_DID_WPT: + ptd->ops = &pch_dev_ops_wpt; + dev_name = "pch_wildcat_point"; + break; + default: + dev_err(&pdev->dev, "unknown pch thermal device\n"); + return -ENODEV; + } + + pci_set_drvdata(pdev, ptd); + ptd->pdev = pdev; + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "failed to enable pci device\n"); + return err; + } + + err = pci_request_regions(pdev, driver_name); + if (err) { + dev_err(&pdev->dev, "failed to request pci region\n"); + goto error_disable; + } + + ptd->hw_base = pci_ioremap_bar(pdev, 0); + if (!ptd->hw_base) { + err = -ENOMEM; + dev_err(&pdev->dev, "failed to map mem base\n"); + goto error_release; + } + + err = ptd->ops->hw_init(ptd, &nr_trips); + if (err) + goto error_cleanup; + + ptd->tzd = thermal_zone_device_register(dev_name, nr_trips, 0, ptd, + &tzd_ops, NULL, 0, 0); + if (IS_ERR(ptd->tzd)) { + dev_err(&pdev->dev, "Failed to register thermal zone %s\n", + dev_name); + err = PTR_ERR(ptd->tzd); + goto error_cleanup; + } + + return 0; + +error_cleanup: + iounmap(ptd->hw_base); +error_release: + pci_release_regions(pdev); +error_disable: + pci_disable_device(pdev); + dev_err(&pdev->dev, "pci device failed to probe\n"); + return err; +} + +static void intel_pch_thermal_remove(struct pci_dev *pdev) +{ + struct pch_thermal_device *ptd = pci_get_drvdata(pdev); + + thermal_zone_device_unregister(ptd->tzd); + iounmap(ptd->hw_base); + pci_set_drvdata(pdev, NULL); + pci_release_region(pdev, 0); + pci_disable_device(pdev); +} + +static struct pci_device_id intel_pch_thermal_id[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT) }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id); + +static struct pci_driver intel_pch_thermal_driver = { + .name = "intel_pch_thermal", + .id_table = intel_pch_thermal_id, + .probe = intel_pch_thermal_probe, + .remove = intel_pch_thermal_remove, +}; + +module_pci_driver(intel_pch_thermal_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel PCH Thermal driver"); diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c index 2ac0c704bcb8..6c79588251d5 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel_powerclamp.c @@ -693,11 +693,14 @@ static const struct x86_cpu_id intel_powerclamp_ids[] __initconst = { { X86_VENDOR_INTEL, 6, 0x3f}, { X86_VENDOR_INTEL, 6, 0x45}, { X86_VENDOR_INTEL, 6, 0x46}, + { X86_VENDOR_INTEL, 6, 0x47}, { X86_VENDOR_INTEL, 6, 0x4c}, { X86_VENDOR_INTEL, 6, 0x4d}, + { X86_VENDOR_INTEL, 6, 0x4e}, { X86_VENDOR_INTEL, 6, 0x4f}, { X86_VENDOR_INTEL, 6, 0x56}, { X86_VENDOR_INTEL, 6, 0x57}, + { X86_VENDOR_INTEL, 6, 0x5e}, {} }; MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids); diff --git a/drivers/thermal/intel_quark_dts_thermal.c b/drivers/thermal/intel_quark_dts_thermal.c index 4434ec812cb7..5ed90e6c8a64 100644 --- a/drivers/thermal/intel_quark_dts_thermal.c +++ b/drivers/thermal/intel_quark_dts_thermal.c @@ -186,7 +186,7 @@ static int soc_dts_disable(struct thermal_zone_device *tzd) return ret; } -static int _get_trip_temp(int trip, unsigned long *temp) +static int _get_trip_temp(int trip, int *temp) { int status; u32 out; @@ -212,19 +212,18 @@ static int _get_trip_temp(int trip, unsigned long *temp) } static inline int sys_get_trip_temp(struct thermal_zone_device *tzd, - int trip, unsigned long *temp) + int trip, int *temp) { return _get_trip_temp(trip, temp); } -static inline int sys_get_crit_temp(struct thermal_zone_device *tzd, - unsigned long *temp) +static inline int sys_get_crit_temp(struct thermal_zone_device *tzd, int *temp) { return _get_trip_temp(QRK_DTS_ID_TP_CRITICAL, temp); } static int update_trip_temp(struct soc_sensor_entry *aux_entry, - int trip, unsigned long temp) + int trip, int temp) { u32 out; u32 temp_out; @@ -272,7 +271,7 @@ failed: } static inline int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, - unsigned long temp) + int temp) { return update_trip_temp(tzd->devdata, trip, temp); } @@ -289,7 +288,7 @@ static int sys_get_trip_type(struct thermal_zone_device *thermal, } static int sys_get_curr_temp(struct thermal_zone_device *tzd, - unsigned long *temp) + int *temp) { u32 out; int ret; diff --git a/drivers/thermal/intel_soc_dts_iosf.c b/drivers/thermal/intel_soc_dts_iosf.c index 42e4b6ac3875..5841d1d72996 100644 --- a/drivers/thermal/intel_soc_dts_iosf.c +++ b/drivers/thermal/intel_soc_dts_iosf.c @@ -80,7 +80,7 @@ err_ret: } static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip, - unsigned long *temp) + int *temp) { int status; u32 out; @@ -106,7 +106,7 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip, } static int update_trip_temp(struct intel_soc_dts_sensor_entry *dts, - int thres_index, unsigned long temp, + int thres_index, int temp, enum thermal_trip_type trip_type) { int status; @@ -196,7 +196,7 @@ err_restore_ptps: } static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, - unsigned long temp) + int temp) { struct intel_soc_dts_sensor_entry *dts = tzd->devdata; struct intel_soc_dts_sensors *sensors = dts->sensors; @@ -226,7 +226,7 @@ static int sys_get_trip_type(struct thermal_zone_device *tzd, } static int sys_get_curr_temp(struct thermal_zone_device *tzd, - unsigned long *temp) + int *temp) { int status; u32 out; diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c index 11041fe63dc2..892236621767 100644 --- a/drivers/thermal/kirkwood_thermal.c +++ b/drivers/thermal/kirkwood_thermal.c @@ -33,7 +33,7 @@ struct kirkwood_thermal_priv { }; static int kirkwood_get_temp(struct thermal_zone_device *thermal, - unsigned long *temp) + int *temp) { unsigned long reg; struct kirkwood_thermal_priv *priv = thermal->devdata; diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index b295b2b6c191..42b7d4253b94 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -91,7 +91,7 @@ struct __thermal_zone { /*** DT thermal zone device callbacks ***/ static int of_thermal_get_temp(struct thermal_zone_device *tz, - unsigned long *temp) + int *temp) { struct __thermal_zone *data = tz->devdata; @@ -177,7 +177,7 @@ EXPORT_SYMBOL_GPL(of_thermal_get_trip_points); * Return: zero on success, error code otherwise */ static int of_thermal_set_emul_temp(struct thermal_zone_device *tz, - unsigned long temp) + int temp) { struct __thermal_zone *data = tz->devdata; @@ -311,7 +311,7 @@ static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip, } static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip, - unsigned long *temp) + int *temp) { struct __thermal_zone *data = tz->devdata; @@ -324,7 +324,7 @@ static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip, } static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip, - unsigned long temp) + int temp) { struct __thermal_zone *data = tz->devdata; @@ -338,7 +338,7 @@ static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip, } static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip, - unsigned long *hyst) + int *hyst) { struct __thermal_zone *data = tz->devdata; @@ -351,7 +351,7 @@ static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip, } static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip, - unsigned long hyst) + int hyst) { struct __thermal_zone *data = tz->devdata; @@ -365,7 +365,7 @@ static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip, } static int of_thermal_get_crit_temp(struct thermal_zone_device *tz, - unsigned long *temp) + int *temp) { struct __thermal_zone *data = tz->devdata; int i; diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c index 251676902869..e570ff084add 100644 --- a/drivers/thermal/power_allocator.c +++ b/drivers/thermal/power_allocator.c @@ -24,6 +24,8 @@ #include "thermal_core.h" +#define INVALID_TRIP -1 + #define FRAC_BITS 10 #define int_to_frac(x) ((x) << FRAC_BITS) #define frac_to_int(x) ((x) >> FRAC_BITS) @@ -56,16 +58,21 @@ static inline s64 div_frac(s64 x, s64 y) /** * struct power_allocator_params - parameters for the power allocator governor + * @allocated_tzp: whether we have allocated tzp for this thermal zone and + * it needs to be freed on unbind * @err_integral: accumulated error in the PID controller. * @prev_err: error in the previous iteration of the PID controller. * Used to calculate the derivative term. * @trip_switch_on: first passive trip point of the thermal zone. The * governor switches on when this trip point is crossed. + * If the thermal zone only has one passive trip point, + * @trip_switch_on should be INVALID_TRIP. * @trip_max_desired_temperature: last passive trip point of the thermal * zone. The temperature we are * controlling for. */ struct power_allocator_params { + bool allocated_tzp; s64 err_integral; s32 prev_err; int trip_switch_on; @@ -73,6 +80,98 @@ struct power_allocator_params { }; /** + * estimate_sustainable_power() - Estimate the sustainable power of a thermal zone + * @tz: thermal zone we are operating in + * + * For thermal zones that don't provide a sustainable_power in their + * thermal_zone_params, estimate one. Calculate it using the minimum + * power of all the cooling devices as that gives a valid value that + * can give some degree of functionality. For optimal performance of + * this governor, provide a sustainable_power in the thermal zone's + * thermal_zone_params. + */ +static u32 estimate_sustainable_power(struct thermal_zone_device *tz) +{ + u32 sustainable_power = 0; + struct thermal_instance *instance; + struct power_allocator_params *params = tz->governor_data; + + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + struct thermal_cooling_device *cdev = instance->cdev; + u32 min_power; + + if (instance->trip != params->trip_max_desired_temperature) + continue; + + if (power_actor_get_min_power(cdev, tz, &min_power)) + continue; + + sustainable_power += min_power; + } + + return sustainable_power; +} + +/** + * estimate_pid_constants() - Estimate the constants for the PID controller + * @tz: thermal zone for which to estimate the constants + * @sustainable_power: sustainable power for the thermal zone + * @trip_switch_on: trip point number for the switch on temperature + * @control_temp: target temperature for the power allocator governor + * @force: whether to force the update of the constants + * + * This function is used to update the estimation of the PID + * controller constants in struct thermal_zone_parameters. + * Sustainable power is provided in case it was estimated. The + * estimated sustainable_power should not be stored in the + * thermal_zone_parameters so it has to be passed explicitly to this + * function. + * + * If @force is not set, the values in the thermal zone's parameters + * are preserved if they are not zero. If @force is set, the values + * in thermal zone's parameters are overwritten. + */ +static void estimate_pid_constants(struct thermal_zone_device *tz, + u32 sustainable_power, int trip_switch_on, + int control_temp, bool force) +{ + int ret; + int switch_on_temp; + u32 temperature_threshold; + + ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp); + if (ret) + switch_on_temp = 0; + + temperature_threshold = control_temp - switch_on_temp; + /* + * estimate_pid_constants() tries to find appropriate default + * values for thermal zones that don't provide them. If a + * system integrator has configured a thermal zone with two + * passive trip points at the same temperature, that person + * hasn't put any effort to set up the thermal zone properly + * so just give up. + */ + if (!temperature_threshold) + return; + + if (!tz->tzp->k_po || force) + tz->tzp->k_po = int_to_frac(sustainable_power) / + temperature_threshold; + + if (!tz->tzp->k_pu || force) + tz->tzp->k_pu = int_to_frac(2 * sustainable_power) / + temperature_threshold; + + if (!tz->tzp->k_i || force) + tz->tzp->k_i = int_to_frac(10) / 1000; + /* + * The default for k_d and integral_cutoff is 0, so we can + * leave them as they are. + */ +} + +/** * pid_controller() - PID controller * @tz: thermal zone we are operating in * @current_temp: the current temperature in millicelsius @@ -92,17 +191,27 @@ struct power_allocator_params { * Return: The power budget for the next period. */ static u32 pid_controller(struct thermal_zone_device *tz, - unsigned long current_temp, - unsigned long control_temp, + int current_temp, + int control_temp, u32 max_allocatable_power) { s64 p, i, d, power_range; s32 err, max_power_frac; + u32 sustainable_power; struct power_allocator_params *params = tz->governor_data; max_power_frac = int_to_frac(max_allocatable_power); - err = ((s32)control_temp - (s32)current_temp); + if (tz->tzp->sustainable_power) { + sustainable_power = tz->tzp->sustainable_power; + } else { + sustainable_power = estimate_sustainable_power(tz); + estimate_pid_constants(tz, sustainable_power, + params->trip_switch_on, control_temp, + true); + } + + err = control_temp - current_temp; err = int_to_frac(err); /* Calculate the proportional term */ @@ -139,7 +248,7 @@ static u32 pid_controller(struct thermal_zone_device *tz, power_range = p + i + d; /* feed-forward the known sustainable dissipatable power */ - power_range = tz->tzp->sustainable_power + frac_to_int(power_range); + power_range = sustainable_power + frac_to_int(power_range); power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power); @@ -223,8 +332,8 @@ static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors, } static int allocate_power(struct thermal_zone_device *tz, - unsigned long current_temp, - unsigned long control_temp) + int current_temp, + int control_temp) { struct thermal_instance *instance; struct power_allocator_params *params = tz->governor_data; @@ -247,6 +356,11 @@ static int allocate_power(struct thermal_zone_device *tz, } } + if (!num_actors) { + ret = -ENODEV; + goto unlock; + } + /* * We need to allocate five arrays of the same size: * req_power, max_power, granted_power, extra_actor_power and @@ -331,7 +445,7 @@ static int allocate_power(struct thermal_zone_device *tz, granted_power, total_granted_power, num_actors, power_range, max_allocatable_power, current_temp, - (s32)control_temp - (s32)current_temp); + control_temp - current_temp); kfree(req_power); unlock: @@ -340,43 +454,66 @@ unlock: return ret; } -static int get_governor_trips(struct thermal_zone_device *tz, - struct power_allocator_params *params) +/** + * get_governor_trips() - get the number of the two trip points that are key for this governor + * @tz: thermal zone to operate on + * @params: pointer to private data for this governor + * + * The power allocator governor works optimally with two trips points: + * a "switch on" trip point and a "maximum desired temperature". These + * are defined as the first and last passive trip points. + * + * If there is only one trip point, then that's considered to be the + * "maximum desired temperature" trip point and the governor is always + * on. If there are no passive or active trip points, then the + * governor won't do anything. In fact, its throttle function + * won't be called at all. + */ +static void get_governor_trips(struct thermal_zone_device *tz, + struct power_allocator_params *params) { - int i, ret, last_passive; + int i, last_active, last_passive; bool found_first_passive; found_first_passive = false; - last_passive = -1; - ret = -EINVAL; + last_active = INVALID_TRIP; + last_passive = INVALID_TRIP; for (i = 0; i < tz->trips; i++) { enum thermal_trip_type type; + int ret; ret = tz->ops->get_trip_type(tz, i, &type); - if (ret) - return ret; + if (ret) { + dev_warn(&tz->device, + "Failed to get trip point %d type: %d\n", i, + ret); + continue; + } - if (!found_first_passive) { - if (type == THERMAL_TRIP_PASSIVE) { + if (type == THERMAL_TRIP_PASSIVE) { + if (!found_first_passive) { params->trip_switch_on = i; found_first_passive = true; + } else { + last_passive = i; } - } else if (type == THERMAL_TRIP_PASSIVE) { - last_passive = i; + } else if (type == THERMAL_TRIP_ACTIVE) { + last_active = i; } else { break; } } - if (last_passive != -1) { + if (last_passive != INVALID_TRIP) { params->trip_max_desired_temperature = last_passive; - ret = 0; + } else if (found_first_passive) { + params->trip_max_desired_temperature = params->trip_switch_on; + params->trip_switch_on = INVALID_TRIP; } else { - ret = -EINVAL; + params->trip_switch_on = INVALID_TRIP; + params->trip_max_desired_temperature = last_active; } - - return ret; } static void reset_pid_controller(struct power_allocator_params *params) @@ -405,60 +542,45 @@ static void allow_maximum_power(struct thermal_zone_device *tz) * power_allocator_bind() - bind the power_allocator governor to a thermal zone * @tz: thermal zone to bind it to * - * Check that the thermal zone is valid for this governor, that is, it - * has two thermal trips. If so, initialize the PID controller - * parameters and bind it to the thermal zone. + * Initialize the PID controller parameters and bind it to the thermal + * zone. * - * Return: 0 on success, -EINVAL if the trips were invalid or -ENOMEM - * if we ran out of memory. + * Return: 0 on success, or -ENOMEM if we ran out of memory. */ static int power_allocator_bind(struct thermal_zone_device *tz) { int ret; struct power_allocator_params *params; - unsigned long switch_on_temp, control_temp; - u32 temperature_threshold; - - if (!tz->tzp || !tz->tzp->sustainable_power) { - dev_err(&tz->device, - "power_allocator: missing sustainable_power\n"); - return -EINVAL; - } + int control_temp; params = kzalloc(sizeof(*params), GFP_KERNEL); if (!params) return -ENOMEM; - ret = get_governor_trips(tz, params); - if (ret) { - dev_err(&tz->device, - "thermal zone %s has wrong trip setup for power allocator\n", - tz->type); - goto free; - } + if (!tz->tzp) { + tz->tzp = kzalloc(sizeof(*tz->tzp), GFP_KERNEL); + if (!tz->tzp) { + ret = -ENOMEM; + goto free_params; + } - ret = tz->ops->get_trip_temp(tz, params->trip_switch_on, - &switch_on_temp); - if (ret) - goto free; + params->allocated_tzp = true; + } - ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature, - &control_temp); - if (ret) - goto free; + if (!tz->tzp->sustainable_power) + dev_warn(&tz->device, "power_allocator: sustainable_power will be estimated\n"); - temperature_threshold = control_temp - switch_on_temp; + get_governor_trips(tz, params); - tz->tzp->k_po = tz->tzp->k_po ?: - int_to_frac(tz->tzp->sustainable_power) / temperature_threshold; - tz->tzp->k_pu = tz->tzp->k_pu ?: - int_to_frac(2 * tz->tzp->sustainable_power) / - temperature_threshold; - tz->tzp->k_i = tz->tzp->k_i ?: int_to_frac(10) / 1000; - /* - * The default for k_d and integral_cutoff is 0, so we can - * leave them as they are. - */ + if (tz->trips > 0) { + ret = tz->ops->get_trip_temp(tz, + params->trip_max_desired_temperature, + &control_temp); + if (!ret) + estimate_pid_constants(tz, tz->tzp->sustainable_power, + params->trip_switch_on, + control_temp, false); + } reset_pid_controller(params); @@ -466,14 +588,23 @@ static int power_allocator_bind(struct thermal_zone_device *tz) return 0; -free: +free_params: kfree(params); + return ret; } static void power_allocator_unbind(struct thermal_zone_device *tz) { + struct power_allocator_params *params = tz->governor_data; + dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id); + + if (params->allocated_tzp) { + kfree(tz->tzp); + tz->tzp = NULL; + } + kfree(tz->governor_data); tz->governor_data = NULL; } @@ -481,7 +612,7 @@ static void power_allocator_unbind(struct thermal_zone_device *tz) static int power_allocator_throttle(struct thermal_zone_device *tz, int trip) { int ret; - unsigned long switch_on_temp, control_temp, current_temp; + int switch_on_temp, control_temp, current_temp; struct power_allocator_params *params = tz->governor_data; /* @@ -499,13 +630,7 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip) ret = tz->ops->get_trip_temp(tz, params->trip_switch_on, &switch_on_temp); - if (ret) { - dev_warn(&tz->device, - "Failed to get switch on temperature: %d\n", ret); - return ret; - } - - if (current_temp < switch_on_temp) { + if (!ret && (current_temp < switch_on_temp)) { tz->passive = 0; reset_pid_controller(params); allow_maximum_power(tz); diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c index c8d27b8fb9ec..b677aada5b52 100644 --- a/drivers/thermal/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom-spmi-temp-alarm.c @@ -117,7 +117,7 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip) return 0; } -static int qpnp_tm_get_temp(void *data, long *temp) +static int qpnp_tm_get_temp(void *data, int *temp) { struct qpnp_tm_chip *chip = data; int ret, mili_celsius; diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index fe4e767018c4..5d4ae7d705e0 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -200,8 +200,7 @@ err_out_unlock: return ret; } -static int rcar_thermal_get_temp(struct thermal_zone_device *zone, - unsigned long *temp) +static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); @@ -235,7 +234,7 @@ static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, } static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, - int trip, unsigned long *temp) + int trip, int *temp) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); struct device *dev = rcar_priv_to_dev(priv); @@ -299,7 +298,7 @@ static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable) static void rcar_thermal_work(struct work_struct *work) { struct rcar_thermal_priv *priv; - unsigned long cctemp, nctemp; + int cctemp, nctemp; priv = container_of(work, struct rcar_thermal_priv, work.work); diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index cd8f5f93b42c..c89ffb26a354 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -64,7 +64,7 @@ struct rockchip_tsadc_chip { void (*control)(void __iomem *reg, bool on); /* Per-sensor methods */ - int (*get_temp)(int chn, void __iomem *reg, long *temp); + int (*get_temp)(int chn, void __iomem *reg, int *temp); void (*set_tshut_temp)(int chn, void __iomem *reg, long temp); void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m); }; @@ -191,7 +191,7 @@ static u32 rk_tsadcv2_temp_to_code(long temp) return 0; } -static long rk_tsadcv2_code_to_temp(u32 code) +static int rk_tsadcv2_code_to_temp(u32 code) { unsigned int low = 0; unsigned int high = ARRAY_SIZE(v2_code_table) - 1; @@ -277,7 +277,7 @@ static void rk_tsadcv2_control(void __iomem *regs, bool enable) writel_relaxed(val, regs + TSADCV2_AUTO_CON); } -static int rk_tsadcv2_get_temp(int chn, void __iomem *regs, long *temp) +static int rk_tsadcv2_get_temp(int chn, void __iomem *regs, int *temp) { u32 val; @@ -366,7 +366,7 @@ static irqreturn_t rockchip_thermal_alarm_irq_thread(int irq, void *dev) return IRQ_HANDLED; } -static int rockchip_thermal_get_temp(void *_sensor, long *out_temp) +static int rockchip_thermal_get_temp(void *_sensor, int *out_temp) { struct rockchip_thermal_sensor *sensor = _sensor; struct rockchip_thermal_data *thermal = sensor->thermal; @@ -374,7 +374,7 @@ static int rockchip_thermal_get_temp(void *_sensor, long *out_temp) int retval; retval = tsadc->get_temp(sensor->id, thermal->regs, out_temp); - dev_dbg(&thermal->pdev->dev, "sensor %d - temp: %ld, retval: %d\n", + dev_dbg(&thermal->pdev->dev, "sensor %d - temp: %d, retval: %d\n", sensor->id, *out_temp, retval); return retval; diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index c96ff10b869e..0bae8cc6c23a 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -207,8 +207,7 @@ struct exynos_tmu_data { int (*tmu_initialize)(struct platform_device *pdev); void (*tmu_control)(struct platform_device *pdev, bool on); int (*tmu_read)(struct exynos_tmu_data *data); - void (*tmu_set_emulation)(struct exynos_tmu_data *data, - unsigned long temp); + void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); void (*tmu_clear_irqs)(struct exynos_tmu_data *data); }; @@ -216,7 +215,7 @@ static void exynos_report_trigger(struct exynos_tmu_data *p) { char data[10], *envp[] = { data, NULL }; struct thermal_zone_device *tz = p->tzd; - unsigned long temp; + int temp; unsigned int i; if (!tz) { @@ -517,7 +516,7 @@ static int exynos5433_tmu_initialize(struct platform_device *pdev) struct thermal_zone_device *tz = data->tzd; unsigned int status, trim_info; unsigned int rising_threshold = 0, falling_threshold = 0; - unsigned long temp, temp_hist; + int temp, temp_hist; int ret = 0, threshold_code, i, sensor_id, cal_type; status = readb(data->base + EXYNOS_TMU_REG_STATUS); @@ -610,7 +609,7 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev) struct exynos_tmu_data *data = platform_get_drvdata(pdev); unsigned int trim_info = 0, con, rising_threshold; int ret = 0, threshold_code; - unsigned long crit_temp = 0; + int crit_temp = 0; /* * For exynos5440 soc triminfo value is swapped between TMU0 and @@ -663,7 +662,7 @@ static int exynos7_tmu_initialize(struct platform_device *pdev) unsigned int status, trim_info; unsigned int rising_threshold = 0, falling_threshold = 0; int ret = 0, threshold_code, i; - unsigned long temp, temp_hist; + int temp, temp_hist; unsigned int reg_off, bit_off; status = readb(data->base + EXYNOS_TMU_REG_STATUS); @@ -876,7 +875,7 @@ static void exynos7_tmu_control(struct platform_device *pdev, bool on) writel(con, data->base + EXYNOS_TMU_REG_CONTROL); } -static int exynos_get_temp(void *p, long *temp) +static int exynos_get_temp(void *p, int *temp) { struct exynos_tmu_data *data = p; @@ -896,7 +895,7 @@ static int exynos_get_temp(void *p, long *temp) #ifdef CONFIG_THERMAL_EMULATION static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, - unsigned long temp) + int temp) { if (temp) { temp /= MCELSIUS; @@ -926,7 +925,7 @@ static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, } static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, - unsigned long temp) + int temp) { unsigned int val; u32 emul_con; @@ -946,7 +945,7 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, } static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data, - unsigned long temp) + int temp) { unsigned int val; @@ -955,7 +954,7 @@ static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data, writel(val, data->base + EXYNOS5440_TMU_S0_7_DEBUG); } -static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) +static int exynos_tmu_set_emulation(void *drv_data, int temp) { struct exynos_tmu_data *data = drv_data; int ret = -EINVAL; @@ -978,7 +977,7 @@ out: #else #define exynos4412_tmu_set_emulation NULL #define exynos5440_tmu_set_emulation NULL -static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) +static int exynos_tmu_set_emulation(void *drv_data, int temp) { return -EINVAL; } #endif /* CONFIG_THERMAL_EMULATION */ diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index bddb71744a6c..534dd9136662 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -38,7 +38,7 @@ struct spear_thermal_dev { }; static inline int thermal_get_temp(struct thermal_zone_device *thermal, - unsigned long *temp) + int *temp) { struct spear_thermal_dev *stdev = thermal->devdata; diff --git a/drivers/thermal/st/st_thermal.c b/drivers/thermal/st/st_thermal.c index 88c759d746c3..be637e6b01d2 100644 --- a/drivers/thermal/st/st_thermal.c +++ b/drivers/thermal/st/st_thermal.c @@ -111,8 +111,7 @@ static int st_thermal_calibration(struct st_thermal_sensor *sensor) } /* Callback to get temperature from HW*/ -static int st_thermal_get_temp(struct thermal_zone_device *th, - unsigned long *temperature) +static int st_thermal_get_temp(struct thermal_zone_device *th, int *temperature) { struct st_thermal_sensor *sensor = th->devdata; struct device *dev = sensor->dev; @@ -159,7 +158,7 @@ static int st_thermal_get_trip_type(struct thermal_zone_device *th, } static int st_thermal_get_trip_temp(struct thermal_zone_device *th, - int trip, unsigned long *temp) + int trip, int *temp) { struct st_thermal_sensor *sensor = th->devdata; struct device *dev = sensor->dev; diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index 5a0f12d08e8b..2f9f7086ac3d 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -113,7 +113,7 @@ static void update_passive_instance(struct thermal_zone_device *tz, static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) { - long trip_temp; + int trip_temp; enum thermal_trip_type trip_type; enum thermal_trend trend; struct thermal_instance *instance; @@ -135,7 +135,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) trace_thermal_zone_trip(tz, trip, trip_type); } - dev_dbg(&tz->device, "Trip%d[type=%d,temp=%ld]:trend=%d,throttle=%d\n", + dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n", trip, trip_type, trip_temp, trend, throttle); mutex_lock(&tz->lock); diff --git a/drivers/thermal/tegra_soctherm.c b/drivers/thermal/tegra_soctherm.c index 9197fc05c5cc..74ea5765938b 100644 --- a/drivers/thermal/tegra_soctherm.c +++ b/drivers/thermal/tegra_soctherm.c @@ -293,7 +293,7 @@ static int enable_tsensor(struct tegra_soctherm *tegra, * H denotes an addition of 0.5 Celsius and N denotes negation * of the final value. */ -static long translate_temp(u16 val) +static int translate_temp(u16 val) { long t; @@ -306,7 +306,7 @@ static long translate_temp(u16 val) return t; } -static int tegra_thermctl_get_temp(void *data, long *out_temp) +static int tegra_thermctl_get_temp(void *data, int *out_temp) { struct tegra_thermctl_zone *zone = data; u32 val; diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 4ca211be4c0f..d9e525cc9c1c 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -426,7 +426,7 @@ static void handle_non_critical_trips(struct thermal_zone_device *tz, static void handle_critical_trips(struct thermal_zone_device *tz, int trip, enum thermal_trip_type trip_type) { - long trip_temp; + int trip_temp; tz->ops->get_trip_temp(tz, trip, &trip_temp); @@ -465,7 +465,7 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) } /** - * thermal_zone_get_temp() - returns its the temperature of thermal zone + * thermal_zone_get_temp() - returns the temperature of a thermal zone * @tz: a valid pointer to a struct thermal_zone_device * @temp: a valid pointer to where to store the resulting temperature. * @@ -474,14 +474,12 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) * * Return: On success returns 0, an error code otherwise */ -int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp) +int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) { int ret = -EINVAL; -#ifdef CONFIG_THERMAL_EMULATION int count; - unsigned long crit_temp = -1UL; + int crit_temp = INT_MAX; enum thermal_trip_type type; -#endif if (!tz || IS_ERR(tz) || !tz->ops->get_temp) goto exit; @@ -489,25 +487,26 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp) mutex_lock(&tz->lock); ret = tz->ops->get_temp(tz, temp); -#ifdef CONFIG_THERMAL_EMULATION - if (!tz->emul_temperature) - goto skip_emul; - - for (count = 0; count < tz->trips; count++) { - ret = tz->ops->get_trip_type(tz, count, &type); - if (!ret && type == THERMAL_TRIP_CRITICAL) { - ret = tz->ops->get_trip_temp(tz, count, &crit_temp); - break; - } - } - if (ret) - goto skip_emul; + if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) { + for (count = 0; count < tz->trips; count++) { + ret = tz->ops->get_trip_type(tz, count, &type); + if (!ret && type == THERMAL_TRIP_CRITICAL) { + ret = tz->ops->get_trip_temp(tz, count, + &crit_temp); + break; + } + } - if (*temp < crit_temp) - *temp = tz->emul_temperature; -skip_emul: -#endif + /* + * Only allow emulating a temperature when the real temperature + * is below the critical temperature so that the emulation code + * cannot hide critical conditions. + */ + if (!ret && *temp < crit_temp) + *temp = tz->emul_temperature; + } + mutex_unlock(&tz->lock); exit: return ret; @@ -516,8 +515,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_temp); static void update_temperature(struct thermal_zone_device *tz) { - long temp; - int ret; + int temp, ret; ret = thermal_zone_get_temp(tz, &temp); if (ret) { @@ -577,15 +575,14 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf) { struct thermal_zone_device *tz = to_thermal_zone(dev); - long temperature; - int ret; + int temperature, ret; ret = thermal_zone_get_temp(tz, &temperature); if (ret) return ret; - return sprintf(buf, "%ld\n", temperature); + return sprintf(buf, "%d\n", temperature); } static ssize_t @@ -689,7 +686,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr, { struct thermal_zone_device *tz = to_thermal_zone(dev); int trip, ret; - long temperature; + int temperature; if (!tz->ops->get_trip_temp) return -EPERM; @@ -702,7 +699,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr, if (ret) return ret; - return sprintf(buf, "%ld\n", temperature); + return sprintf(buf, "%d\n", temperature); } static ssize_t @@ -711,7 +708,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, { struct thermal_zone_device *tz = to_thermal_zone(dev); int trip, ret; - unsigned long temperature; + int temperature; if (!tz->ops->set_trip_hyst) return -EPERM; @@ -719,7 +716,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip)) return -EINVAL; - if (kstrtoul(buf, 10, &temperature)) + if (kstrtoint(buf, 10, &temperature)) return -EINVAL; /* @@ -738,7 +735,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr, { struct thermal_zone_device *tz = to_thermal_zone(dev); int trip, ret; - unsigned long temperature; + int temperature; if (!tz->ops->get_trip_hyst) return -EPERM; @@ -748,7 +745,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr, ret = tz->ops->get_trip_hyst(tz, trip, &temperature); - return ret ? ret : sprintf(buf, "%ld\n", temperature); + return ret ? ret : sprintf(buf, "%d\n", temperature); } static ssize_t @@ -847,7 +844,27 @@ policy_show(struct device *dev, struct device_attribute *devattr, char *buf) return sprintf(buf, "%s\n", tz->governor->name); } -#ifdef CONFIG_THERMAL_EMULATION +static ssize_t +available_policies_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct thermal_governor *pos; + ssize_t count = 0; + ssize_t size = PAGE_SIZE; + + mutex_lock(&thermal_governor_lock); + + list_for_each_entry(pos, &thermal_governor_list, governor_list) { + size = PAGE_SIZE - count; + count += scnprintf(buf + count, size, "%s ", pos->name); + } + count += scnprintf(buf + count, size, "\n"); + + mutex_unlock(&thermal_governor_lock); + + return count; +} + static ssize_t emul_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -873,7 +890,6 @@ emul_temp_store(struct device *dev, struct device_attribute *attr, return ret ? ret : count; } static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); -#endif/*CONFIG_THERMAL_EMULATION*/ static ssize_t sustainable_power_show(struct device *dev, struct device_attribute *devattr, @@ -997,6 +1013,34 @@ int power_actor_get_max_power(struct thermal_cooling_device *cdev, } /** + * power_actor_get_min_power() - get the mainimum power that a cdev can consume + * @cdev: pointer to &thermal_cooling_device + * @tz: a valid thermal zone device pointer + * @min_power: pointer in which to store the minimum power + * + * Calculate the minimum power consumption in milliwatts that the + * cooling device can currently consume and store it in @min_power. + * + * Return: 0 on success, -EINVAL if @cdev doesn't support the + * power_actor API or -E* on other error. + */ +int power_actor_get_min_power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, u32 *min_power) +{ + unsigned long max_state; + int ret; + + if (!cdev_is_power_actor(cdev)) + return -EINVAL; + + ret = cdev->ops->get_max_state(cdev, &max_state); + if (ret) + return ret; + + return cdev->ops->state2power(cdev, tz, max_state, min_power); +} + +/** * power_actor_set_power() - limit the maximum power that a cooling device can consume * @cdev: pointer to &thermal_cooling_device * @instance: thermal instance to update @@ -1032,6 +1076,7 @@ static DEVICE_ATTR(temp, 0444, temp_show, NULL); static DEVICE_ATTR(mode, 0644, mode_show, mode_store); static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); +static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL); /* sys I/F for cooling device */ #define to_cooling_device(_dev) \ @@ -1803,11 +1848,12 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, goto unregister; } -#ifdef CONFIG_THERMAL_EMULATION - result = device_create_file(&tz->device, &dev_attr_emul_temp); - if (result) - goto unregister; -#endif + if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) { + result = device_create_file(&tz->device, &dev_attr_emul_temp); + if (result) + goto unregister; + } + /* Create policy attribute */ result = device_create_file(&tz->device, &dev_attr_policy); if (result) @@ -1818,6 +1864,11 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, if (result) goto unregister; + /* Create available_policies attribute */ + result = device_create_file(&tz->device, &dev_attr_available_policies); + if (result) + goto unregister; + /* Update 'this' zone's governor information */ mutex_lock(&thermal_governor_lock); @@ -1849,9 +1900,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check); - if (!tz->ops->get_temp) - thermal_zone_device_set_polling(tz, 0); - thermal_zone_device_update(tz); return tz; @@ -1918,6 +1966,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) if (tz->ops->get_mode) device_remove_file(&tz->device, &dev_attr_mode); device_remove_file(&tz->device, &dev_attr_policy); + device_remove_file(&tz->device, &dev_attr_available_policies); remove_trip_attrs(tz); thermal_set_governor(tz, NULL); diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index 1967bee4f076..06fd2ed9ef9d 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -69,7 +69,7 @@ static DEVICE_ATTR(name, 0444, name_show, NULL); static ssize_t temp_input_show(struct device *dev, struct device_attribute *attr, char *buf) { - long temperature; + int temperature; int ret; struct thermal_hwmon_attr *hwmon_attr = container_of(attr, struct thermal_hwmon_attr, attr); @@ -83,7 +83,7 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf) if (ret) return ret; - return sprintf(buf, "%ld\n", temperature); + return sprintf(buf, "%d\n", temperature); } static ssize_t @@ -95,14 +95,14 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) = container_of(hwmon_attr, struct thermal_hwmon_temp, temp_crit); struct thermal_zone_device *tz = temp->tz; - long temperature; + int temperature; int ret; ret = tz->ops->get_trip_temp(tz, 0, &temperature); if (ret) return ret; - return sprintf(buf, "%ld\n", temperature); + return sprintf(buf, "%d\n", temperature); } @@ -142,7 +142,7 @@ thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon, static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz) { - unsigned long temp; + int temp; return tz->ops->get_crit_temp && !tz->ops->get_crit_temp(tz, &temp); } diff --git a/drivers/thermal/ti-soc-thermal/Kconfig b/drivers/thermal/ti-soc-thermal/Kconfig index bd4c7beba679..cb6686ff09ae 100644 --- a/drivers/thermal/ti-soc-thermal/Kconfig +++ b/drivers/thermal/ti-soc-thermal/Kconfig @@ -1,7 +1,5 @@ config TI_SOC_THERMAL tristate "Texas Instruments SoCs temperature sensor driver" - depends on THERMAL - depends on ARCH_HAS_BANDGAP help If you say yes here you get support for the Texas Instruments OMAP4460+ on die bandgap temperature sensor support. The register @@ -24,7 +22,7 @@ config TI_THERMAL config OMAP4_THERMAL bool "Texas Instruments OMAP4 thermal support" depends on TI_SOC_THERMAL - depends on ARCH_OMAP4 + depends on ARCH_OMAP4 || COMPILE_TEST help If you say yes here you get thermal support for the Texas Instruments OMAP4 SoC family. The current chip supported are: @@ -38,7 +36,7 @@ config OMAP4_THERMAL config OMAP5_THERMAL bool "Texas Instruments OMAP5 thermal support" depends on TI_SOC_THERMAL - depends on SOC_OMAP5 + depends on SOC_OMAP5 || COMPILE_TEST help If you say yes here you get thermal support for the Texas Instruments OMAP5 SoC family. The current chip supported are: @@ -50,7 +48,7 @@ config OMAP5_THERMAL config DRA752_THERMAL bool "Texas Instruments DRA752 thermal support" depends on TI_SOC_THERMAL - depends on SOC_DRA7XX + depends on SOC_DRA7XX || COMPILE_TEST help If you say yes here you get thermal support for the Texas Instruments DRA752 SoC family. The current chip supported are: diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index c7c5b3779dac..b213a1222295 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -76,14 +76,14 @@ static inline int ti_thermal_hotspot_temperature(int t, int s, int c) /* thermal zone ops */ /* Get temperature callback function for thermal zone */ -static inline int __ti_thermal_get_temp(void *devdata, long *temp) +static inline int __ti_thermal_get_temp(void *devdata, int *temp) { struct thermal_zone_device *pcb_tz = NULL; struct ti_thermal_data *data = devdata; struct ti_bandgap *bgp; const struct ti_temp_sensor *s; int ret, tmp, slope, constant; - unsigned long pcb_temp; + int pcb_temp; if (!data) return 0; @@ -119,7 +119,7 @@ static inline int __ti_thermal_get_temp(void *devdata, long *temp) } static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal, - unsigned long *temp) + int *temp) { struct ti_thermal_data *data = thermal->devdata; @@ -229,7 +229,7 @@ static int ti_thermal_get_trip_type(struct thermal_zone_device *thermal, /* Get trip temperature callback functions for thermal zone */ static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal, - int trip, unsigned long *temp) + int trip, int *temp) { if (!ti_thermal_is_valid_trip(trip)) return -EINVAL; @@ -280,7 +280,7 @@ static int ti_thermal_get_trend(struct thermal_zone_device *thermal, /* Get critical temperature callback functions for thermal zone */ static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal, - unsigned long *temp) + int *temp) { /* shutdown zone */ return ti_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp); diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 50d1d2cb091a..7fc919f7da4d 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -164,7 +164,7 @@ err_ret: return err; } -static int sys_get_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp) +static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp) { u32 eax, edx; struct phy_dev_entry *phy_dev_entry; @@ -175,7 +175,7 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd, unsigned long *tem if (eax & 0x80000000) { *temp = phy_dev_entry->tj_max - ((eax >> 16) & 0x7f) * 1000; - pr_debug("sys_get_curr_temp %ld\n", *temp); + pr_debug("sys_get_curr_temp %d\n", *temp); return 0; } @@ -183,7 +183,7 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd, unsigned long *tem } static int sys_get_trip_temp(struct thermal_zone_device *tzd, - int trip, unsigned long *temp) + int trip, int *temp) { u32 eax, edx; struct phy_dev_entry *phy_dev_entry; @@ -214,13 +214,13 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd, *temp = phy_dev_entry->tj_max - thres_reg_value * 1000; else *temp = 0; - pr_debug("sys_get_trip_temp %ld\n", *temp); + pr_debug("sys_get_trip_temp %d\n", *temp); return 0; } static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, - unsigned long temp) + int temp) { u32 l, h; struct phy_dev_entry *phy_dev_entry; diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index c68fe1222c16..20a41f7de76f 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -643,7 +643,7 @@ static struct pci_device_id nhi_ids[] = { { .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, .vendor = PCI_VENDOR_ID_INTEL, .device = 0x156c, - .subvendor = 0x2222, .subdevice = 0x1111, + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, { 0,} }; diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index bb809cf36617..8b70a1627356 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -88,8 +88,8 @@ struct iucv_tty_buffer { }; /* IUCV callback handler */ -static int hvc_iucv_path_pending(struct iucv_path *, u8[8], u8[16]); -static void hvc_iucv_path_severed(struct iucv_path *, u8[16]); +static int hvc_iucv_path_pending(struct iucv_path *, u8 *, u8 *); +static void hvc_iucv_path_severed(struct iucv_path *, u8 *); static void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *); static void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *); @@ -782,8 +782,8 @@ static int hvc_iucv_filter_connreq(u8 ipvmid[8]) * * Locking: struct hvc_iucv_private->lock */ -static int hvc_iucv_path_pending(struct iucv_path *path, - u8 ipvmid[8], u8 ipuser[16]) +static int hvc_iucv_path_pending(struct iucv_path *path, u8 *ipvmid, + u8 *ipuser) { struct hvc_iucv_private *priv, *tmp; u8 wildcard[9] = "lnxhvc "; @@ -881,7 +881,7 @@ out_path_handled: * * Locking: struct hvc_iucv_private->lock */ -static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) +static void hvc_iucv_path_severed(struct iucv_path *path, u8 *ipuser) { struct hvc_iucv_private *priv = path->private; diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 54e6c8ddef5d..b1e0ba3e525b 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2910,3 +2910,5 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe) } #endif /* CONFIG_SERIAL_8250_CONSOLE */ + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 867e9f3f3859..dcc50c878159 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -61,7 +61,7 @@ static const struct of_device_id ci_hdrc_imx_dt_ids[] = { { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, - { .compatible = "fsl,imx6sx-usb", .data = &imx6sl_usb_data}, + { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index 9eae1a16cef9..4456d2cf80ff 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -12,6 +12,7 @@ #include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/usb/chipidea.h> @@ -30,18 +31,36 @@ static const struct ci_hdrc_platform_data ci_default_pdata = { .flags = CI_HDRC_DISABLE_STREAMING, }; +static struct ci_hdrc_platform_data ci_zynq_pdata = { + .capoffset = DEF_CAPOFFSET, +}; + +static const struct of_device_id ci_hdrc_usb2_of_match[] = { + { .compatible = "chipidea,usb2"}, + { .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata}, + { } +}; +MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match); + static int ci_hdrc_usb2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ci_hdrc_usb2_priv *priv; struct ci_hdrc_platform_data *ci_pdata = dev_get_platdata(dev); int ret; + const struct of_device_id *match; if (!ci_pdata) { ci_pdata = devm_kmalloc(dev, sizeof(*ci_pdata), GFP_KERNEL); *ci_pdata = ci_default_pdata; /* struct copy */ } + match = of_match_device(ci_hdrc_usb2_of_match, &pdev->dev); + if (match && match->data) { + /* struct copy */ + *ci_pdata = *(struct ci_hdrc_platform_data *)match->data; + } + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -96,12 +115,6 @@ static int ci_hdrc_usb2_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id ci_hdrc_usb2_of_match[] = { - { .compatible = "chipidea,usb2" }, - { } -}; -MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match); - static struct platform_driver ci_hdrc_usb2_driver = { .probe = ci_hdrc_usb2_probe, .remove = ci_hdrc_usb2_remove, diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index a637da25dda0..8223fe73ea85 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -656,6 +656,44 @@ __acquires(hwep->lock) return 0; } +static int _ep_set_halt(struct usb_ep *ep, int value, bool check_transfer) +{ + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); + int direction, retval = 0; + unsigned long flags; + + if (ep == NULL || hwep->ep.desc == NULL) + return -EINVAL; + + if (usb_endpoint_xfer_isoc(hwep->ep.desc)) + return -EOPNOTSUPP; + + spin_lock_irqsave(hwep->lock, flags); + + if (value && hwep->dir == TX && check_transfer && + !list_empty(&hwep->qh.queue) && + !usb_endpoint_xfer_control(hwep->ep.desc)) { + spin_unlock_irqrestore(hwep->lock, flags); + return -EAGAIN; + } + + direction = hwep->dir; + do { + retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value); + + if (!value) + hwep->wedge = 0; + + if (hwep->type == USB_ENDPOINT_XFER_CONTROL) + hwep->dir = (hwep->dir == TX) ? RX : TX; + + } while (hwep->dir != direction); + + spin_unlock_irqrestore(hwep->lock, flags); + return retval; +} + + /** * _gadget_stop_activity: stops all USB activity, flushes & disables all endpts * @gadget: gadget @@ -1051,7 +1089,7 @@ __acquires(ci->lock) num += ci->hw_ep_max / 2; spin_unlock(&ci->lock); - err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep); + err = _ep_set_halt(&ci->ci_hw_ep[num].ep, 1, false); spin_lock(&ci->lock); if (!err) isr_setup_status_phase(ci); @@ -1117,8 +1155,8 @@ delegate: if (err < 0) { spin_unlock(&ci->lock); - if (usb_ep_set_halt(&hwep->ep)) - dev_err(ci->dev, "error: ep_set_halt\n"); + if (_ep_set_halt(&hwep->ep, 1, false)) + dev_err(ci->dev, "error: _ep_set_halt\n"); spin_lock(&ci->lock); } } @@ -1149,9 +1187,9 @@ __acquires(ci->lock) err = isr_setup_status_phase(ci); if (err < 0) { spin_unlock(&ci->lock); - if (usb_ep_set_halt(&hwep->ep)) + if (_ep_set_halt(&hwep->ep, 1, false)) dev_err(ci->dev, - "error: ep_set_halt\n"); + "error: _ep_set_halt\n"); spin_lock(&ci->lock); } } @@ -1397,41 +1435,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) */ static int ep_set_halt(struct usb_ep *ep, int value) { - struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); - int direction, retval = 0; - unsigned long flags; - - if (ep == NULL || hwep->ep.desc == NULL) - return -EINVAL; - - if (usb_endpoint_xfer_isoc(hwep->ep.desc)) - return -EOPNOTSUPP; - - spin_lock_irqsave(hwep->lock, flags); - -#ifndef STALL_IN - /* g_file_storage MS compliant but g_zero fails chapter 9 compliance */ - if (value && hwep->type == USB_ENDPOINT_XFER_BULK && hwep->dir == TX && - !list_empty(&hwep->qh.queue)) { - spin_unlock_irqrestore(hwep->lock, flags); - return -EAGAIN; - } -#endif - - direction = hwep->dir; - do { - retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value); - - if (!value) - hwep->wedge = 0; - - if (hwep->type == USB_ENDPOINT_XFER_CONTROL) - hwep->dir = (hwep->dir == TX) ? RX : TX; - - } while (hwep->dir != direction); - - spin_unlock_irqrestore(hwep->lock, flags); - return retval; + return _ep_set_halt(ep, value, true); } /** diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index b2a540b43f97..b9ddf0c1ffe5 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -112,7 +112,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 16; } else if (usb_endpoint_xfer_isoc(&ep->desc) && - desc->bmAttributes > 2) { + USB_SS_MULT(desc->bmAttributes) > 3) { dev_warn(ddev, "Isoc endpoint has Mult of %d in " "config %d interface %d altsetting %d ep %d: " "setting to 3\n", desc->bmAttributes + 1, @@ -121,7 +121,8 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, } if (usb_endpoint_xfer_isoc(&ep->desc)) - max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) * + max_tx = (desc->bMaxBurst + 1) * + (USB_SS_MULT(desc->bmAttributes)) * usb_endpoint_maxp(&ep->desc); else if (usb_endpoint_xfer_int(&ep->desc)) max_tx = usb_endpoint_maxp(&ep->desc) * diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index a5a1b7c45743..22e9606d8e08 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -514,8 +514,6 @@ static int dwc3_omap_probe(struct platform_device *pdev) goto err1; } - dwc3_omap_enable_irqs(omap); - ret = dwc3_omap_extcon_register(omap); if (ret < 0) goto err2; @@ -526,6 +524,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) goto err3; } + dwc3_omap_enable_irqs(omap); + return 0; err3: diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0c25704dcb6b..1e8bdf817811 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2665,8 +2665,6 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc) int i; irqreturn_t ret = IRQ_NONE; - spin_lock(&dwc->lock); - for (i = 0; i < dwc->num_event_buffers; i++) { irqreturn_t status; @@ -2675,8 +2673,6 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc) ret = status; } - spin_unlock(&dwc->lock); - return ret; } diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 978435a51038..6399c106a3a5 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -186,6 +186,7 @@ void usb_ep_autoconfig_reset (struct usb_gadget *gadget) list_for_each_entry (ep, &gadget->ep_list, ep_list) { ep->claimed = false; + ep->driver_data = NULL; } gadget->in_epnum = 0; gadget->out_epnum = 0; diff --git a/drivers/usb/gadget/udc/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c index fdacddb18c00..175ca93fe5e2 100644 --- a/drivers/usb/gadget/udc/amd5536udc.c +++ b/drivers/usb/gadget/udc/amd5536udc.c @@ -3138,8 +3138,8 @@ static void udc_pci_remove(struct pci_dev *pdev) writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); if (dev->irq_registered) free_irq(pdev->irq, dev); - if (dev->regs) - iounmap(dev->regs); + if (dev->virt_addr) + iounmap(dev->virt_addr); if (dev->mem_region) release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); @@ -3226,17 +3226,13 @@ static int udc_pci_probe( /* init */ dev = kzalloc(sizeof(struct udc), GFP_KERNEL); - if (!dev) { - retval = -ENOMEM; - goto finished; - } + if (!dev) + return -ENOMEM; /* pci setup */ if (pci_enable_device(pdev) < 0) { - kfree(dev); - dev = NULL; retval = -ENODEV; - goto finished; + goto err_pcidev; } dev->active = 1; @@ -3246,28 +3242,22 @@ static int udc_pci_probe( if (!request_mem_region(resource, len, name)) { dev_dbg(&pdev->dev, "pci device used already\n"); - kfree(dev); - dev = NULL; retval = -EBUSY; - goto finished; + goto err_memreg; } dev->mem_region = 1; dev->virt_addr = ioremap_nocache(resource, len); if (dev->virt_addr == NULL) { dev_dbg(&pdev->dev, "start address cannot be mapped\n"); - kfree(dev); - dev = NULL; retval = -EFAULT; - goto finished; + goto err_ioremap; } if (!pdev->irq) { dev_err(&pdev->dev, "irq not set\n"); - kfree(dev); - dev = NULL; retval = -ENODEV; - goto finished; + goto err_irq; } spin_lock_init(&dev->lock); @@ -3283,10 +3273,8 @@ static int udc_pci_probe( if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) { dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq); - kfree(dev); - dev = NULL; retval = -EBUSY; - goto finished; + goto err_irq; } dev->irq_registered = 1; @@ -3314,8 +3302,17 @@ static int udc_pci_probe( return 0; finished: - if (dev) - udc_pci_remove(pdev); + udc_pci_remove(pdev); + return retval; + +err_irq: + iounmap(dev->virt_addr); +err_ioremap: + release_mem_region(resource, len); +err_memreg: + pci_disable_device(pdev); +err_pcidev: + kfree(dev); return retval; } diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 3dfada8d6061..f0f2b066ac08 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -2002,6 +2002,17 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, ep->udc = udc; INIT_LIST_HEAD(&ep->queue); + if (ep->index == 0) { + ep->ep.caps.type_control = true; + } else { + ep->ep.caps.type_iso = ep->can_isoc; + ep->ep.caps.type_bulk = true; + ep->ep.caps.type_int = true; + } + + ep->ep.caps.dir_in = true; + ep->ep.caps.dir_out = true; + if (i) list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 5c8f4effb62a..ccb9c213cc9f 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -324,8 +324,7 @@ static void bdc_mem_free(struct bdc *bdc) bdc->scratchpad.buff, bdc->scratchpad.sp_dma); /* Destroy the dma pools */ - if (bdc->bd_table_pool) - dma_pool_destroy(bdc->bd_table_pool); + dma_pool_destroy(bdc->bd_table_pool); /* Free the bdc_ep array */ kfree(bdc->bdc_ep_array); diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 1379ad40d864..27af0f008b57 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -1348,6 +1348,7 @@ static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb, { struct dummy *dum = dum_hcd->dum; struct dummy_request *req; + int sent = 0; top: /* if there's no request queued, the device is NAKing; return */ @@ -1385,12 +1386,15 @@ top: if (len == 0) break; - /* use an extra pass for the final short packet */ - if (len > ep->ep.maxpacket) { - rescan = 1; - len -= (len % ep->ep.maxpacket); + /* send multiple of maxpacket first, then remainder */ + if (len >= ep->ep.maxpacket) { + is_short = 0; + if (len % ep->ep.maxpacket) + rescan = 1; + len -= len % ep->ep.maxpacket; + } else { + is_short = 1; } - is_short = (len % ep->ep.maxpacket) != 0; len = dummy_perform_transfer(urb, req, len); @@ -1399,6 +1403,7 @@ top: req->req.status = len; } else { limit -= len; + sent += len; urb->actual_length += len; req->req.actual += len; } @@ -1421,7 +1426,7 @@ top: *status = -EOVERFLOW; else *status = 0; - } else if (!to_host) { + } else { *status = 0; if (host_len > dev_len) req->req.status = -EOVERFLOW; @@ -1429,15 +1434,24 @@ top: req->req.status = 0; } - /* many requests terminate without a short packet */ + /* + * many requests terminate without a short packet. + * send a zlp if demanded by flags. + */ } else { - if (req->req.length == req->req.actual - && !req->req.zero) - req->req.status = 0; - if (urb->transfer_buffer_length == urb->actual_length - && !(urb->transfer_flags - & URB_ZERO_PACKET)) - *status = 0; + if (req->req.length == req->req.actual) { + if (req->req.zero && to_host) + rescan = 1; + else + req->req.status = 0; + } + if (urb->transfer_buffer_length == urb->actual_length) { + if (urb->transfer_flags & URB_ZERO_PACKET && + !to_host) + rescan = 1; + else + *status = 0; + } } /* device side completion --> continuable */ @@ -1460,7 +1474,7 @@ top: if (rescan) goto top; } - return limit; + return sent; } static int periodic_bytes(struct dummy *dum, struct dummy_ep *ep) @@ -1890,7 +1904,7 @@ restart: default: treat_control_like_bulk: ep->last_io = jiffies; - total = transfer(dum_hcd, urb, ep, limit, &status); + total -= transfer(dum_hcd, urb, ep, limit, &status); break; } diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 8aa2593c2c36..b9429bc42511 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2117,8 +2117,7 @@ static int gr_remove(struct platform_device *pdev) return -EBUSY; gr_dfs_delete(dev); - if (dev->desc_pool) - dma_pool_destroy(dev->desc_pool); + dma_pool_destroy(dev->desc_pool); platform_set_drvdata(pdev, NULL); gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req); diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index 4c489692745e..dafe74eb9ade 100644 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -1767,8 +1767,7 @@ static int mv_u3d_remove(struct platform_device *dev) usb_del_gadget_udc(&u3d->gadget); /* free memory allocated in probe */ - if (u3d->trb_pool) - dma_pool_destroy(u3d->trb_pool); + dma_pool_destroy(u3d->trb_pool); if (u3d->ep_context) dma_free_coherent(&dev->dev, u3d->ep_context_size, diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 339af51df57d..81b6229c7805 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -2100,8 +2100,7 @@ static int mv_udc_remove(struct platform_device *pdev) } /* free memory allocated in probe */ - if (udc->dtd_pool) - dma_pool_destroy(udc->dtd_pool); + dma_pool_destroy(udc->dtd_pool); if (udc->ep_dqh) dma_free_coherent(&pdev->dev, udc->ep_dqh_size, diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 9a8c936cd42c..41f841fa6c4d 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1498,10 +1498,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, * use Event Data TRBs, and we don't chain in a link TRB on short * transfers, we're basically dividing by 1. * - * xHCI 1.0 specification indicates that the Average TRB Length should - * be set to 8 for control endpoints. + * xHCI 1.0 and 1.1 specification indicates that the Average TRB Length + * should be set to 8 for control endpoints. */ - if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version == 0x100) + if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version >= 0x100) ep_ctx->tx_info |= cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(8)); else ep_ctx->tx_info |= @@ -1792,8 +1792,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) int size; int i, j, num_ports; - if (timer_pending(&xhci->cmd_timer)) - del_timer_sync(&xhci->cmd_timer); + del_timer_sync(&xhci->cmd_timer); /* Free the Event Ring Segment Table and the actual Event Ring */ size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); @@ -2321,6 +2320,10 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) INIT_LIST_HEAD(&xhci->cmd_list); + /* init command timeout timer */ + setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout, + (unsigned long)xhci); + page_size = readl(&xhci->op_regs->page_size); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Supported page size register = 0x%x", page_size); @@ -2505,10 +2508,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) "Wrote ERST address to ir_set 0."); xhci_print_ir_set(xhci, 0); - /* init command timeout timer */ - setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout, - (unsigned long)xhci); - /* * XXX: Might need to set the Interrupter Moderation Register to * something other than the default (~1ms minimum between interrupts). diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 5590eac2b22d..c79d33676672 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -180,51 +180,6 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) "QUIRK: Resetting on resume"); } -/* - * In some Intel xHCI controllers, in order to get D3 working, - * through a vendor specific SSIC CONFIG register at offset 0x883c, - * SSIC PORT need to be marked as "unused" before putting xHCI - * into D3. After D3 exit, the SSIC port need to be marked as "used". - * Without this change, xHCI might not enter D3 state. - * Make sure PME works on some Intel xHCI controllers by writing 1 to clear - * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4 - */ -static void xhci_pme_quirk(struct usb_hcd *hcd, bool suspend) -{ - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - u32 val; - void __iomem *reg; - - if (pdev->vendor == PCI_VENDOR_ID_INTEL && - pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { - - reg = (void __iomem *) xhci->cap_regs + PORT2_SSIC_CONFIG_REG2; - - /* Notify SSIC that SSIC profile programming is not done */ - val = readl(reg) & ~PROG_DONE; - writel(val, reg); - - /* Mark SSIC port as unused(suspend) or used(resume) */ - val = readl(reg); - if (suspend) - val |= SSIC_PORT_UNUSED; - else - val &= ~SSIC_PORT_UNUSED; - writel(val, reg); - - /* Notify SSIC that SSIC profile programming is done */ - val = readl(reg) | PROG_DONE; - writel(val, reg); - readl(reg); - } - - reg = (void __iomem *) xhci->cap_regs + 0x80a4; - val = readl(reg); - writel(val | BIT(28), reg); - readl(reg); -} - #ifdef CONFIG_ACPI static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { @@ -345,6 +300,51 @@ static void xhci_pci_remove(struct pci_dev *dev) } #ifdef CONFIG_PM +/* + * In some Intel xHCI controllers, in order to get D3 working, + * through a vendor specific SSIC CONFIG register at offset 0x883c, + * SSIC PORT need to be marked as "unused" before putting xHCI + * into D3. After D3 exit, the SSIC port need to be marked as "used". + * Without this change, xHCI might not enter D3 state. + * Make sure PME works on some Intel xHCI controllers by writing 1 to clear + * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4 + */ +static void xhci_pme_quirk(struct usb_hcd *hcd, bool suspend) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + u32 val; + void __iomem *reg; + + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { + + reg = (void __iomem *) xhci->cap_regs + PORT2_SSIC_CONFIG_REG2; + + /* Notify SSIC that SSIC profile programming is not done */ + val = readl(reg) & ~PROG_DONE; + writel(val, reg); + + /* Mark SSIC port as unused(suspend) or used(resume) */ + val = readl(reg); + if (suspend) + val |= SSIC_PORT_UNUSED; + else + val &= ~SSIC_PORT_UNUSED; + writel(val, reg); + + /* Notify SSIC that SSIC profile programming is done */ + val = readl(reg) | PROG_DONE; + writel(val, reg); + readl(reg); + } + + reg = (void __iomem *) xhci->cap_regs + 0x80a4; + val = readl(reg); + writel(val | BIT(28), reg); + readl(reg); +} + static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index a47a1e897086..43291f93afeb 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -302,6 +302,15 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci) ret = xhci_handshake(&xhci->op_regs->cmd_ring, CMD_RING_RUNNING, 0, 5 * 1000 * 1000); if (ret < 0) { + /* we are about to kill xhci, give it one more chance */ + xhci_write_64(xhci, temp_64 | CMD_RING_ABORT, + &xhci->op_regs->cmd_ring); + udelay(1000); + ret = xhci_handshake(&xhci->op_regs->cmd_ring, + CMD_RING_RUNNING, 0, 3 * 1000 * 1000); + if (ret == 0) + return 0; + xhci_err(xhci, "Stopped the command ring failed, " "maybe the host is dead\n"); xhci->xhc_state |= XHCI_STATE_DYING; @@ -3461,8 +3470,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (start_cycle == 0) field |= 0x1; - /* xHCI 1.0 6.4.1.2.1: Transfer Type field */ - if (xhci->hci_version == 0x100) { + /* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */ + if (xhci->hci_version >= 0x100) { if (urb->transfer_buffer_length > 0) { if (setup->bRequestType & USB_DIR_IN) field |= TRB_TX_TYPE(TRB_DATA_IN); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6b0f4a47e402..9957bd96d4bc 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -146,7 +146,8 @@ static int xhci_start(struct xhci_hcd *xhci) "waited %u microseconds.\n", XHCI_MAX_HALT_USEC); if (!ret) - xhci->xhc_state &= ~XHCI_STATE_HALTED; + xhci->xhc_state &= ~(XHCI_STATE_HALTED | XHCI_STATE_DYING); + return ret; } @@ -654,15 +655,6 @@ int xhci_run(struct usb_hcd *hcd) } EXPORT_SYMBOL_GPL(xhci_run); -static void xhci_only_stop_hcd(struct usb_hcd *hcd) -{ - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - - spin_lock_irq(&xhci->lock); - xhci_halt(xhci); - spin_unlock_irq(&xhci->lock); -} - /* * Stop xHCI driver. * @@ -677,12 +669,14 @@ void xhci_stop(struct usb_hcd *hcd) u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - if (!usb_hcd_is_primary_hcd(hcd)) { - xhci_only_stop_hcd(xhci->shared_hcd); + if (xhci->xhc_state & XHCI_STATE_HALTED) return; - } + mutex_lock(&xhci->mutex); spin_lock_irq(&xhci->lock); + xhci->xhc_state |= XHCI_STATE_HALTED; + xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; + /* Make sure the xHC is halted for a USB3 roothub * (xhci_stop() could be called as part of failed init). */ @@ -717,6 +711,7 @@ void xhci_stop(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_stop completed - status = %x", readl(&xhci->op_regs->status)); + mutex_unlock(&xhci->mutex); } /* @@ -3793,6 +3788,9 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, mutex_lock(&xhci->mutex); + if (xhci->xhc_state) /* dying or halted */ + goto out; + if (!udev->slot_id) { xhci_dbg_trace(xhci, trace_xhci_dbg_address, "Bad Slot ID %d", udev->slot_id); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 514a6cdaeff6..4a518ff12310 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1051,6 +1051,7 @@ void musb_start(struct musb *musb) * (c) peripheral initiates, using SRP */ if (musb->port_mode != MUSB_PORT_MODE_HOST && + musb->xceiv->otg->state != OTG_STATE_A_WAIT_BCON && (devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) { musb->is_active = 1; } else { @@ -2448,6 +2449,9 @@ static int musb_suspend(struct device *dev) struct musb *musb = dev_to_musb(dev); unsigned long flags; + musb_platform_disable(musb); + musb_generic_disable(musb); + spin_lock_irqsave(&musb->lock, flags); if (is_peripheral_active(musb)) { @@ -2501,6 +2505,9 @@ static int musb_resume(struct device *dev) pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); + + musb_start(musb); + return 0; } diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index d07cafb7d5f5..e499b862a946 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -551,6 +551,9 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel) } else { cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREQ_NONE); + /* delay to drain to cppi dma pipeline for isoch */ + udelay(250); + csr = musb_readw(epio, MUSB_RXCSR); csr &= ~(MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_DMAENAB); musb_writew(epio, MUSB_RXCSR, csr); diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index a0cfead6150f..84512d1d5eee 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -225,8 +225,11 @@ static void dsps_musb_enable(struct musb *musb) dsps_writel(reg_base, wrp->epintr_set, epmask); dsps_writel(reg_base, wrp->coreintr_set, coremask); - /* start polling for ID change. */ - mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout)); + /* start polling for ID change in dual-role idle mode */ + if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && + musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(wrp->poll_timeout)); dsps_musb_try_idle(musb, 0); } diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 39168fe9b406..b2685e75a683 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -379,6 +379,8 @@ static const struct of_device_id ux500_match[] = { {} }; +MODULE_DEVICE_TABLE(of, ux500_match); + static struct platform_driver ux500_driver = { .probe = ux500_probe, .remove = ux500_remove, diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 7d3beee2a587..173132416170 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -155,7 +155,7 @@ config USB_MSM_OTG config USB_QCOM_8X16_PHY tristate "Qualcomm APQ8016/MSM8916 on-chip USB PHY controller support" depends on ARCH_QCOM || COMPILE_TEST - depends on RESET_CONTROLLER + depends on RESET_CONTROLLER && EXTCON select USB_PHY select USB_ULPI_VIEWPORT help diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index ec6ecd03269c..5320cb8642cb 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -232,7 +232,8 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, clk_rate = pdata->clk_rate; needs_vcc = pdata->needs_vcc; if (gpio_is_valid(pdata->gpio_reset)) { - err = devm_gpio_request_one(dev, pdata->gpio_reset, 0, + err = devm_gpio_request_one(dev, pdata->gpio_reset, + GPIOF_ACTIVE_LOW, dev_name(dev)); if (!err) nop->gpiod_reset = diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index 8a55b37d1a02..db68156568e6 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -31,6 +31,7 @@ static const struct i2c_device_id isp1301_id[] = { { "isp1301", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, isp1301_id); static struct i2c_client *isp1301_i2c_client; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 6d1941a2396a..6956c4f62216 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -278,6 +278,10 @@ static void option_instat_callback(struct urb *urb); #define ZTE_PRODUCT_MF622 0x0001 #define ZTE_PRODUCT_MF628 0x0015 #define ZTE_PRODUCT_MF626 0x0031 +#define ZTE_PRODUCT_ZM8620_X 0x0396 +#define ZTE_PRODUCT_ME3620_MBIM 0x0426 +#define ZTE_PRODUCT_ME3620_X 0x1432 +#define ZTE_PRODUCT_ME3620_L 0x1433 #define ZTE_PRODUCT_AC2726 0xfff1 #define ZTE_PRODUCT_MG880 0xfffd #define ZTE_PRODUCT_CDMA_TECH 0xfffe @@ -544,6 +548,18 @@ static const struct option_blacklist_info zte_mc2716_z_blacklist = { .sendsetup = BIT(1) | BIT(2) | BIT(3), }; +static const struct option_blacklist_info zte_me3620_mbim_blacklist = { + .reserved = BIT(2) | BIT(3) | BIT(4), +}; + +static const struct option_blacklist_info zte_me3620_xl_blacklist = { + .reserved = BIT(3) | BIT(4) | BIT(5), +}; + +static const struct option_blacklist_info zte_zm8620_x_blacklist = { + .reserved = BIT(3) | BIT(4) | BIT(5), +}; + static const struct option_blacklist_info huawei_cdc12_blacklist = { .reserved = BIT(1) | BIT(2), }; @@ -1591,6 +1607,14 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist }, + { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_L), + .driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist }, + { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_MBIM), + .driver_info = (kernel_ulong_t)&zte_me3620_mbim_blacklist }, + { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_X), + .driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist }, + { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ZM8620_X), + .driver_info = (kernel_ulong_t)&zte_zm8620_x_blacklist }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) }, diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 6c3734d2b45a..d3ea90bef84d 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -80,6 +80,8 @@ static int whiteheat_firmware_download(struct usb_serial *serial, static int whiteheat_firmware_attach(struct usb_serial *serial); /* function prototypes for the Connect Tech WhiteHEAT serial converter */ +static int whiteheat_probe(struct usb_serial *serial, + const struct usb_device_id *id); static int whiteheat_attach(struct usb_serial *serial); static void whiteheat_release(struct usb_serial *serial); static int whiteheat_port_probe(struct usb_serial_port *port); @@ -116,6 +118,7 @@ static struct usb_serial_driver whiteheat_device = { .description = "Connect Tech - WhiteHEAT", .id_table = id_table_std, .num_ports = 4, + .probe = whiteheat_probe, .attach = whiteheat_attach, .release = whiteheat_release, .port_probe = whiteheat_port_probe, @@ -217,6 +220,34 @@ static int whiteheat_firmware_attach(struct usb_serial *serial) /***************************************************************************** * Connect Tech's White Heat serial driver functions *****************************************************************************/ + +static int whiteheat_probe(struct usb_serial *serial, + const struct usb_device_id *id) +{ + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + size_t num_bulk_in = 0; + size_t num_bulk_out = 0; + size_t min_num_bulk; + unsigned int i; + + iface_desc = serial->interface->cur_altsetting; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; + if (usb_endpoint_is_bulk_in(endpoint)) + ++num_bulk_in; + if (usb_endpoint_is_bulk_out(endpoint)) + ++num_bulk_out; + } + + min_num_bulk = COMMAND_PORT + 1; + if (num_bulk_in < min_num_bulk || num_bulk_out < min_num_bulk) + return -ENODEV; + + return 0; +} + static int whiteheat_attach(struct usb_serial *serial) { struct usb_serial_port *command_port; diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 7d137a43cc86..9eda69e40678 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -61,8 +61,7 @@ MODULE_PARM_DESC(experimental_zcopytx, "Enable Zero Copy TX;" enum { VHOST_NET_FEATURES = VHOST_FEATURES | (1ULL << VHOST_NET_F_VIRTIO_NET_HDR) | - (1ULL << VIRTIO_NET_F_MRG_RXBUF) | - (1ULL << VIRTIO_F_VERSION_1), + (1ULL << VIRTIO_NET_F_MRG_RXBUF) }; enum { diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index f114a9dbb48f..e25a23692822 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -166,9 +166,7 @@ enum { /* Note: can't set VIRTIO_F_VERSION_1 yet, since that implies ANY_LAYOUT. */ enum { VHOST_SCSI_FEATURES = VHOST_FEATURES | (1ULL << VIRTIO_SCSI_F_HOTPLUG) | - (1ULL << VIRTIO_SCSI_F_T10_PI) | - (1ULL << VIRTIO_F_ANY_LAYOUT) | - (1ULL << VIRTIO_F_VERSION_1) + (1ULL << VIRTIO_SCSI_F_T10_PI) }; #define VHOST_SCSI_MAX_TARGET 256 diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c index d9c501eaa6c3..f2882ac98726 100644 --- a/drivers/vhost/test.c +++ b/drivers/vhost/test.c @@ -277,10 +277,13 @@ static long vhost_test_ioctl(struct file *f, unsigned int ioctl, return -EFAULT; return 0; case VHOST_SET_FEATURES: + printk(KERN_ERR "1\n"); if (copy_from_user(&features, featurep, sizeof features)) return -EFAULT; + printk(KERN_ERR "2\n"); if (features & ~VHOST_FEATURES) return -EOPNOTSUPP; + printk(KERN_ERR "3\n"); return vhost_test_set_features(n, features); case VHOST_RESET_OWNER: return vhost_test_reset_owner(n); diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index ce6f6da4b09f..4772862b71a7 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -173,7 +173,9 @@ enum { VHOST_FEATURES = (1ULL << VIRTIO_F_NOTIFY_ON_EMPTY) | (1ULL << VIRTIO_RING_F_INDIRECT_DESC) | (1ULL << VIRTIO_RING_F_EVENT_IDX) | - (1ULL << VHOST_F_LOG_ALL), + (1ULL << VHOST_F_LOG_ALL) | + (1ULL << VIRTIO_F_ANY_LAYOUT) | + (1ULL << VIRTIO_F_VERSION_1) }; static inline bool vhost_has_feature(struct vhost_virtqueue *vq, int bit) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 55c4b5b0a317..79e1aa1b0959 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -188,6 +188,15 @@ config AT91SAM9X_WATCHDOG Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will reboot your system when the timeout is reached. +config SAMA5D4_WATCHDOG + tristate "Atmel SAMA5D4 Watchdog Timer" + depends on ARCH_AT91 + select WATCHDOG_CORE + help + Atmel SAMA5D4 watchdog timer is embedded into SAMA5D4 chips. + Its Watchdog Timer Mode Register can be written more than once. + This will reboot your system when the timeout is reached. + config CADENCE_WATCHDOG tristate "Cadence Watchdog Timer" depends on HAS_IOMEM @@ -558,6 +567,17 @@ config DIGICOLOR_WATCHDOG To compile this driver as a module, choose M here: the module will be called digicolor_wdt. +config LPC18XX_WATCHDOG + tristate "LPC18xx/43xx Watchdog" + depends on ARCH_LPC18XX || COMPILE_TEST + select WATCHDOG_CORE + help + Say Y here if to include support for the watchdog timer + in NXP LPC SoCs family, which includes LPC18xx/LPC43xx + processors. + To compile this driver as a module, choose M here: the + module will be called lpc18xx_wdt. + # AVR32 Architecture config AT32AP700X_WDT @@ -797,8 +817,9 @@ config ITCO_WDT tristate "Intel TCO Timer/Watchdog" depends on (X86 || IA64) && PCI select WATCHDOG_CORE + depends on I2C || I2C=n select LPC_ICH if !EXPERT - select I2C_I801 if !EXPERT + select I2C_I801 if !EXPERT && I2C ---help--- Hardware driver for the intel TCO timer based watchdog devices. These drivers are included in the Intel 82801 I/O Controller @@ -1334,7 +1355,7 @@ config MPC5200_WDT config 8xxx_WDT tristate "MPC8xxx Platform Watchdog Timer" - depends on PPC_8xx || PPC_83xx || PPC_86xx + depends on PPC_8xx || PPC_83xx || PPC_86xx || PPC_MPC512x select WATCHDOG_CORE help This driver is for a SoC level watchdog that exists on some diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 59ea9a1b8e76..0c616e3f67bb 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o +obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o @@ -66,6 +67,7 @@ obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o +obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index 9ba1153465ae..e12a797cb820 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -244,7 +244,7 @@ static int at91wdt_probe(struct platform_device *pdev) } regmap_st = syscon_node_to_regmap(parent->of_node); - if (!regmap_st) + if (IS_ERR(regmap_st)) return -ENODEV; res = misc_register(&at91wdt_miscdev); diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index e4698f7c5f93..7e6acaf3ece4 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -17,6 +17,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/clk.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -90,6 +91,7 @@ struct at91wdt { unsigned long heartbeat; /* WDT heartbeat in jiffies */ bool nowayout; unsigned int irq; + struct clk *sclk; }; /* ......................................................................... */ @@ -352,15 +354,25 @@ static int __init at91wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt->base)) return PTR_ERR(wdt->base); + wdt->sclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(wdt->sclk)) + return PTR_ERR(wdt->sclk); + + err = clk_prepare_enable(wdt->sclk); + if (err) { + dev_err(&pdev->dev, "Could not enable slow clock\n"); + return err; + } + if (pdev->dev.of_node) { err = of_at91wdt_init(pdev->dev.of_node, wdt); if (err) - return err; + goto err_clk; } err = at91_wdt_init(pdev, wdt); if (err) - return err; + goto err_clk; platform_set_drvdata(pdev, wdt); @@ -368,6 +380,11 @@ static int __init at91wdt_probe(struct platform_device *pdev) wdt->wdd.timeout, wdt->nowayout); return 0; + +err_clk: + clk_disable_unprepare(wdt->sclk); + + return err; } static int __exit at91wdt_remove(struct platform_device *pdev) @@ -377,6 +394,7 @@ static int __exit at91wdt_remove(struct platform_device *pdev) pr_warn("I quit now, hardware will probably reboot!\n"); del_timer(&wdt->timer); + clk_disable_unprepare(wdt->sclk); return 0; } diff --git a/drivers/watchdog/at91sam9_wdt.h b/drivers/watchdog/at91sam9_wdt.h index c6fbb2e6c41b..b79a83b467ce 100644 --- a/drivers/watchdog/at91sam9_wdt.h +++ b/drivers/watchdog/at91sam9_wdt.h @@ -22,11 +22,13 @@ #define AT91_WDT_MR 0x04 /* Watchdog Mode Register */ #define AT91_WDT_WDV (0xfff << 0) /* Counter Value */ +#define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV) #define AT91_WDT_WDFIEN (1 << 12) /* Fault Interrupt Enable */ #define AT91_WDT_WDRSTEN (1 << 13) /* Reset Processor */ #define AT91_WDT_WDRPROC (1 << 14) /* Timer Restart */ #define AT91_WDT_WDDIS (1 << 15) /* Watchdog Disable */ #define AT91_WDT_WDD (0xfff << 16) /* Delta Value */ +#define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD) #define AT91_WDT_WDDBGHLT (1 << 28) /* Debug Halt */ #define AT91_WDT_WDIDLEHLT (1 << 29) /* Idle Halt */ diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index 7116968dee12..8a5ce5b5a0b6 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -36,6 +36,13 @@ #define PM_RSTC_WRCFG_FULL_RESET 0x00000020 #define PM_RSTC_RESET 0x00000102 +/* + * The Raspberry Pi firmware uses the RSTS register to know which partiton + * to boot from. The partiton value is spread into bits 0, 2, 4, 6, 8, 10. + * Partiton 63 is a special partition used by the firmware to indicate halt. + */ +#define PM_RSTS_RASPBERRYPI_HALT 0x555 + #define SECS_TO_WDOG_TICKS(x) ((x) << 16) #define WDOG_TICKS_TO_SECS(x) ((x) >> 16) @@ -151,8 +158,7 @@ static void bcm2835_power_off(void) * hard reset. */ val = readl_relaxed(wdt->base + PM_RSTS); - val &= PM_RSTC_WRCFG_CLR; - val |= PM_PASSWORD | PM_RSTS_HADWRH_SET; + val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT; writel_relaxed(val, wdt->base + PM_RSTS); /* Continue with normal reset mechanism */ @@ -182,6 +188,7 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt); watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev); watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout); + bcm2835_wdt_wdd.parent = &pdev->dev; err = watchdog_register_device(&bcm2835_wdt_wdd); if (err) { dev_err(dev, "Failed to register watchdog device"); diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index b28a072abf78..4064a43f1360 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -209,6 +209,7 @@ static int bcm47xx_wdt_probe(struct platform_device *pdev) wdt->wdd.info = &bcm47xx_wdt_info; wdt->wdd.timeout = WDT_DEFAULT_TIME; + wdt->wdd.parent = &pdev->dev; ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout); if (ret) goto err_timer; diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c index 22d8ae65772a..e0c98423f2c9 100644 --- a/drivers/watchdog/bcm_kona_wdt.c +++ b/drivers/watchdog/bcm_kona_wdt.c @@ -319,6 +319,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) spin_lock_init(&wdt->lock); platform_set_drvdata(pdev, wdt); watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt); + bcm_kona_wdt_wdd.parent = &pdev->dev; ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0); if (ret) { diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index e96b09b135c8..04da4b66c75e 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -186,8 +186,6 @@ static int booke_wdt_stop(struct watchdog_device *wdog) static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev, unsigned int timeout) { - if (timeout > MAX_WDT_TIMEOUT) - return -EINVAL; wdt_dev->timeout = timeout; booke_wdt_set(wdt_dev); @@ -211,7 +209,6 @@ static struct watchdog_device booke_wdt_dev = { .info = &booke_wdt_info, .ops = &booke_wdt_ops, .min_timeout = 1, - .max_timeout = 0xFFFF }; static void __exit booke_wdt_exit(void) @@ -229,6 +226,7 @@ static int __init booke_wdt_init(void) booke_wdt_set_timeout(&booke_wdt_dev, period_to_sec(booke_wdt_period)); watchdog_set_nowayout(&booke_wdt_dev, nowayout); + booke_wdt_dev.max_timeout = MAX_WDT_TIMEOUT; if (booke_wdt_enabled) booke_wdt_start(&booke_wdt_dev); diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c index ce12f437f195..a099b77fc0b9 100644 --- a/drivers/watchdog/coh901327_wdt.c +++ b/drivers/watchdog/coh901327_wdt.c @@ -358,6 +358,7 @@ static int __init coh901327_probe(struct platform_device *pdev) if (ret < 0) coh901327_wdt.timeout = 60; + coh901327_wdt.parent = &pdev->dev; ret = watchdog_register_device(&coh901327_wdt); if (ret == 0) dev_info(&pdev->dev, diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c index 2e9589652e1e..67e67977bd29 100644 --- a/drivers/watchdog/da9052_wdt.c +++ b/drivers/watchdog/da9052_wdt.c @@ -195,6 +195,7 @@ static int da9052_wdt_probe(struct platform_device *pdev) da9052_wdt->timeout = DA9052_DEF_TIMEOUT; da9052_wdt->info = &da9052_wdt_info; da9052_wdt->ops = &da9052_wdt_ops; + da9052_wdt->parent = &pdev->dev; watchdog_set_drvdata(da9052_wdt, driver_data); kref_init(&driver_data->kref); diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c index 495089d8dbfe..04d1430d93d2 100644 --- a/drivers/watchdog/da9055_wdt.c +++ b/drivers/watchdog/da9055_wdt.c @@ -161,6 +161,7 @@ static int da9055_wdt_probe(struct platform_device *pdev) da9055_wdt->timeout = DA9055_DEF_TIMEOUT; da9055_wdt->info = &da9055_wdt_info; da9055_wdt->ops = &da9055_wdt_ops; + da9055_wdt->parent = &pdev->dev; watchdog_set_nowayout(da9055_wdt, nowayout); watchdog_set_drvdata(da9055_wdt, driver_data); diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index b3a870ce85be..7386111220d5 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -210,6 +210,7 @@ static int da9062_wdt_probe(struct platform_device *pdev) wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT; wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT; wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; + wdt->wdtdev.parent = &pdev->dev; watchdog_set_drvdata(&wdt->wdtdev, wdt); dev_set_drvdata(&pdev->dev, wdt); diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index e2fe2ebdebd4..6bf130bd863d 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c @@ -175,6 +175,7 @@ static int da9063_wdt_probe(struct platform_device *pdev) wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT; wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT; wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT; + wdt->wdtdev.parent = &pdev->dev; wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index cfdf8a408aea..17454ca653f4 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -179,6 +179,7 @@ static int davinci_wdt_probe(struct platform_device *pdev) wdd->min_timeout = 1; wdd->max_timeout = MAX_HEARTBEAT; wdd->timeout = DEFAULT_HEARTBEAT; + wdd->parent = &pdev->dev; watchdog_init_timeout(wdd, heartbeat, dev); diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c index 31d8e4936611..50abe1bf62a5 100644 --- a/drivers/watchdog/digicolor_wdt.c +++ b/drivers/watchdog/digicolor_wdt.c @@ -143,6 +143,7 @@ static int dc_wdt_probe(struct platform_device *pdev) } dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk); dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout; + dc_wdt_wdd.parent = &pdev->dev; spin_lock_init(&wdt->lock); diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c index 7a2cc7191c58..0a4d7cc05d54 100644 --- a/drivers/watchdog/ep93xx_wdt.c +++ b/drivers/watchdog/ep93xx_wdt.c @@ -132,6 +132,7 @@ static int ep93xx_wdt_probe(struct platform_device *pdev) val = readl(mmio_base + EP93XX_WATCHDOG); ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0; ep93xx_wdt_wdd.timeout = timeout; + ep93xx_wdt_wdd.parent = &pdev->dev; watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout); diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c index cc1bdfc2ff71..006e2348022c 100644 --- a/drivers/watchdog/gef_wdt.c +++ b/drivers/watchdog/gef_wdt.c @@ -303,6 +303,7 @@ static const struct of_device_id gef_wdt_ids[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, gef_wdt_ids); static struct platform_driver gef_wdt_driver = { .driver = { diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index 1687cc2d7122..90d59d3f38a3 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -50,12 +50,41 @@ static void gpio_wdt_disable(struct gpio_wdt_priv *priv) gpio_direction_input(priv->gpio); } +static void gpio_wdt_hwping(unsigned long data) +{ + struct watchdog_device *wdd = (struct watchdog_device *)data; + struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); + + if (priv->armed && time_after(jiffies, priv->last_jiffies + + msecs_to_jiffies(wdd->timeout * 1000))) { + dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n"); + return; + } + + /* Restart timer */ + mod_timer(&priv->timer, jiffies + priv->hw_margin); + + switch (priv->hw_algo) { + case HW_ALGO_TOGGLE: + /* Toggle output pin */ + priv->state = !priv->state; + gpio_set_value_cansleep(priv->gpio, priv->state); + break; + case HW_ALGO_LEVEL: + /* Pulse */ + gpio_set_value_cansleep(priv->gpio, !priv->active_low); + udelay(1); + gpio_set_value_cansleep(priv->gpio, priv->active_low); + break; + } +} + static void gpio_wdt_start_impl(struct gpio_wdt_priv *priv) { priv->state = priv->active_low; gpio_direction_output(priv->gpio, priv->state); priv->last_jiffies = jiffies; - mod_timer(&priv->timer, priv->last_jiffies + priv->hw_margin); + gpio_wdt_hwping((unsigned long)&priv->wdd); } static int gpio_wdt_start(struct watchdog_device *wdd) @@ -97,35 +126,6 @@ static int gpio_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) return gpio_wdt_ping(wdd); } -static void gpio_wdt_hwping(unsigned long data) -{ - struct watchdog_device *wdd = (struct watchdog_device *)data; - struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); - - if (priv->armed && time_after(jiffies, priv->last_jiffies + - msecs_to_jiffies(wdd->timeout * 1000))) { - dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n"); - return; - } - - /* Restart timer */ - mod_timer(&priv->timer, jiffies + priv->hw_margin); - - switch (priv->hw_algo) { - case HW_ALGO_TOGGLE: - /* Toggle output pin */ - priv->state = !priv->state; - gpio_set_value_cansleep(priv->gpio, priv->state); - break; - case HW_ALGO_LEVEL: - /* Pulse */ - gpio_set_value_cansleep(priv->gpio, !priv->active_low); - udelay(1); - gpio_set_value_cansleep(priv->gpio, priv->active_low); - break; - } -} - static int gpio_wdt_notify_sys(struct notifier_block *nb, unsigned long code, void *unused) { @@ -182,10 +182,10 @@ static int gpio_wdt_probe(struct platform_device *pdev) ret = of_property_read_string(pdev->dev.of_node, "hw_algo", &algo); if (ret) return ret; - if (!strncmp(algo, "toggle", 6)) { + if (!strcmp(algo, "toggle")) { priv->hw_algo = HW_ALGO_TOGGLE; f = GPIOF_IN; - } else if (!strncmp(algo, "level", 5)) { + } else if (!strcmp(algo, "level")) { priv->hw_algo = HW_ALGO_LEVEL; f = priv->active_low ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; } else { @@ -217,6 +217,7 @@ static int gpio_wdt_probe(struct platform_device *pdev) priv->wdd.ops = &gpio_wdt_ops; priv->wdd.min_timeout = SOFT_TIMEOUT_MIN; priv->wdd.max_timeout = SOFT_TIMEOUT_MAX; + priv->wdd.parent = &pdev->dev; if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0) priv->wdd.timeout = SOFT_TIMEOUT_DEF; diff --git a/drivers/watchdog/ie6xx_wdt.c b/drivers/watchdog/ie6xx_wdt.c index 9bc39ae51624..78c2541f5d52 100644 --- a/drivers/watchdog/ie6xx_wdt.c +++ b/drivers/watchdog/ie6xx_wdt.c @@ -267,6 +267,7 @@ static int ie6xx_wdt_probe(struct platform_device *pdev) ie6xx_wdt_dev.timeout = timeout; watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout); + ie6xx_wdt_dev.parent = &pdev->dev; spin_lock_init(&ie6xx_wdt_data.unlock_sequence); diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c index 0f73621827ab..15ab07230960 100644 --- a/drivers/watchdog/imgpdc_wdt.c +++ b/drivers/watchdog/imgpdc_wdt.c @@ -316,6 +316,7 @@ static int pdc_wdt_remove(struct platform_device *pdev) { struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev); + unregister_restart_handler(&pdc_wdt->restart_handler); pdc_wdt_stop(&pdc_wdt->wdt_dev); watchdog_unregister_device(&pdc_wdt->wdt_dev); clk_disable_unprepare(pdc_wdt->wdt_clk); diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index 84f6701c391f..0a436b5d1e84 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -137,6 +137,7 @@ static int mid_wdt_probe(struct platform_device *pdev) wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN; wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX; wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT; + wdt_dev->parent = &pdev->dev; watchdog_set_drvdata(wdt_dev, &pdev->dev); platform_set_drvdata(pdev, wdt_dev); diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c index 4c2cc09c0c57..6a7d5c365438 100644 --- a/drivers/watchdog/jz4740_wdt.c +++ b/drivers/watchdog/jz4740_wdt.c @@ -174,6 +174,7 @@ static int jz4740_wdt_probe(struct platform_device *pdev) jz4740_wdt->timeout = heartbeat; jz4740_wdt->min_timeout = 1; jz4740_wdt->max_timeout = MAX_HEARTBEAT; + jz4740_wdt->parent = &pdev->dev; watchdog_set_nowayout(jz4740_wdt, nowayout); watchdog_set_drvdata(jz4740_wdt, drvdata); diff --git a/drivers/watchdog/lpc18xx_wdt.c b/drivers/watchdog/lpc18xx_wdt.c new file mode 100644 index 000000000000..ab7b8b185d99 --- /dev/null +++ b/drivers/watchdog/lpc18xx_wdt.c @@ -0,0 +1,340 @@ +/* + * NXP LPC18xx Watchdog Timer (WDT) + * + * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * Notes + * ----- + * The Watchdog consists of a fixed divide-by-4 clock pre-scaler and a 24-bit + * counter which decrements on every clock cycle. + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/watchdog.h> + +/* Registers */ +#define LPC18XX_WDT_MOD 0x00 +#define LPC18XX_WDT_MOD_WDEN BIT(0) +#define LPC18XX_WDT_MOD_WDRESET BIT(1) + +#define LPC18XX_WDT_TC 0x04 +#define LPC18XX_WDT_TC_MIN 0xff +#define LPC18XX_WDT_TC_MAX 0xffffff + +#define LPC18XX_WDT_FEED 0x08 +#define LPC18XX_WDT_FEED_MAGIC1 0xaa +#define LPC18XX_WDT_FEED_MAGIC2 0x55 + +#define LPC18XX_WDT_TV 0x0c + +/* Clock pre-scaler */ +#define LPC18XX_WDT_CLK_DIV 4 + +/* Timeout values in seconds */ +#define LPC18XX_WDT_DEF_TIMEOUT 30U + +static int heartbeat; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds (default=" + __MODULE_STRING(LPC18XX_WDT_DEF_TIMEOUT) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct lpc18xx_wdt_dev { + struct watchdog_device wdt_dev; + struct clk *reg_clk; + struct clk *wdt_clk; + unsigned long clk_rate; + void __iomem *base; + struct timer_list timer; + struct notifier_block restart_handler; + spinlock_t lock; +}; + +static int lpc18xx_wdt_feed(struct watchdog_device *wdt_dev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); + unsigned long flags; + + /* + * An abort condition will occur if an interrupt happens during the feed + * sequence. + */ + spin_lock_irqsave(&lpc18xx_wdt->lock, flags); + writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags); + + return 0; +} + +static void lpc18xx_wdt_timer_feed(unsigned long data) +{ + struct watchdog_device *wdt_dev = (struct watchdog_device *)data; + struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); + + lpc18xx_wdt_feed(wdt_dev); + + /* Use safe value (1/2 of real timeout) */ + mod_timer(&lpc18xx_wdt->timer, jiffies + + msecs_to_jiffies((wdt_dev->timeout * MSEC_PER_SEC) / 2)); +} + +/* + * Since LPC18xx Watchdog cannot be disabled in hardware, we must keep feeding + * it with a timer until userspace watchdog software takes over. + */ +static int lpc18xx_wdt_stop(struct watchdog_device *wdt_dev) +{ + lpc18xx_wdt_timer_feed((unsigned long)wdt_dev); + + return 0; +} + +static void __lpc18xx_wdt_set_timeout(struct lpc18xx_wdt_dev *lpc18xx_wdt) +{ + unsigned int val; + + val = DIV_ROUND_UP(lpc18xx_wdt->wdt_dev.timeout * lpc18xx_wdt->clk_rate, + LPC18XX_WDT_CLK_DIV); + writel(val, lpc18xx_wdt->base + LPC18XX_WDT_TC); +} + +static int lpc18xx_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int new_timeout) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); + + lpc18xx_wdt->wdt_dev.timeout = new_timeout; + __lpc18xx_wdt_set_timeout(lpc18xx_wdt); + + return 0; +} + +static unsigned int lpc18xx_wdt_get_timeleft(struct watchdog_device *wdt_dev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); + unsigned int val; + + val = readl(lpc18xx_wdt->base + LPC18XX_WDT_TV); + return (val * LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate; +} + +static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); + unsigned int val; + + if (timer_pending(&lpc18xx_wdt->timer)) + del_timer(&lpc18xx_wdt->timer); + + val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD); + val |= LPC18XX_WDT_MOD_WDEN; + val |= LPC18XX_WDT_MOD_WDRESET; + writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD); + + /* + * Setting the WDEN bit in the WDMOD register is not sufficient to + * enable the Watchdog. A valid feed sequence must be completed after + * setting WDEN before the Watchdog is capable of generating a reset. + */ + lpc18xx_wdt_feed(wdt_dev); + + return 0; +} + +static struct watchdog_info lpc18xx_wdt_info = { + .identity = "NXP LPC18xx Watchdog", + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops lpc18xx_wdt_ops = { + .owner = THIS_MODULE, + .start = lpc18xx_wdt_start, + .stop = lpc18xx_wdt_stop, + .ping = lpc18xx_wdt_feed, + .set_timeout = lpc18xx_wdt_set_timeout, + .get_timeleft = lpc18xx_wdt_get_timeleft, +}; + +static int lpc18xx_wdt_restart(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = container_of(this, + struct lpc18xx_wdt_dev, restart_handler); + unsigned long flags; + int val; + + /* + * Incorrect feed sequence causes immediate watchdog reset if enabled. + */ + spin_lock_irqsave(&lpc18xx_wdt->lock, flags); + + val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD); + val |= LPC18XX_WDT_MOD_WDEN; + val |= LPC18XX_WDT_MOD_WDRESET; + writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD); + + writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + + writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED); + + spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags); + + return NOTIFY_OK; +} + +static int lpc18xx_wdt_probe(struct platform_device *pdev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + + lpc18xx_wdt = devm_kzalloc(dev, sizeof(*lpc18xx_wdt), GFP_KERNEL); + if (!lpc18xx_wdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + lpc18xx_wdt->base = devm_ioremap_resource(dev, res); + if (IS_ERR(lpc18xx_wdt->base)) + return PTR_ERR(lpc18xx_wdt->base); + + lpc18xx_wdt->reg_clk = devm_clk_get(dev, "reg"); + if (IS_ERR(lpc18xx_wdt->reg_clk)) { + dev_err(dev, "failed to get the reg clock\n"); + return PTR_ERR(lpc18xx_wdt->reg_clk); + } + + lpc18xx_wdt->wdt_clk = devm_clk_get(dev, "wdtclk"); + if (IS_ERR(lpc18xx_wdt->wdt_clk)) { + dev_err(dev, "failed to get the wdt clock\n"); + return PTR_ERR(lpc18xx_wdt->wdt_clk); + } + + ret = clk_prepare_enable(lpc18xx_wdt->reg_clk); + if (ret) { + dev_err(dev, "could not prepare or enable sys clock\n"); + return ret; + } + + ret = clk_prepare_enable(lpc18xx_wdt->wdt_clk); + if (ret) { + dev_err(dev, "could not prepare or enable wdt clock\n"); + goto disable_reg_clk; + } + + /* We use the clock rate to calculate timeouts */ + lpc18xx_wdt->clk_rate = clk_get_rate(lpc18xx_wdt->wdt_clk); + if (lpc18xx_wdt->clk_rate == 0) { + dev_err(dev, "failed to get clock rate\n"); + ret = -EINVAL; + goto disable_wdt_clk; + } + + lpc18xx_wdt->wdt_dev.info = &lpc18xx_wdt_info; + lpc18xx_wdt->wdt_dev.ops = &lpc18xx_wdt_ops; + + lpc18xx_wdt->wdt_dev.min_timeout = DIV_ROUND_UP(LPC18XX_WDT_TC_MIN * + LPC18XX_WDT_CLK_DIV, lpc18xx_wdt->clk_rate); + + lpc18xx_wdt->wdt_dev.max_timeout = (LPC18XX_WDT_TC_MAX * + LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate; + + lpc18xx_wdt->wdt_dev.timeout = min(lpc18xx_wdt->wdt_dev.max_timeout, + LPC18XX_WDT_DEF_TIMEOUT); + + spin_lock_init(&lpc18xx_wdt->lock); + + lpc18xx_wdt->wdt_dev.parent = dev; + watchdog_set_drvdata(&lpc18xx_wdt->wdt_dev, lpc18xx_wdt); + + ret = watchdog_init_timeout(&lpc18xx_wdt->wdt_dev, heartbeat, dev); + + __lpc18xx_wdt_set_timeout(lpc18xx_wdt); + + setup_timer(&lpc18xx_wdt->timer, lpc18xx_wdt_timer_feed, + (unsigned long)&lpc18xx_wdt->wdt_dev); + + watchdog_set_nowayout(&lpc18xx_wdt->wdt_dev, nowayout); + + platform_set_drvdata(pdev, lpc18xx_wdt); + + ret = watchdog_register_device(&lpc18xx_wdt->wdt_dev); + if (ret) + goto disable_wdt_clk; + + lpc18xx_wdt->restart_handler.notifier_call = lpc18xx_wdt_restart; + lpc18xx_wdt->restart_handler.priority = 128; + ret = register_restart_handler(&lpc18xx_wdt->restart_handler); + if (ret) + dev_warn(dev, "failed to register restart handler: %d\n", ret); + + return 0; + +disable_wdt_clk: + clk_disable_unprepare(lpc18xx_wdt->wdt_clk); +disable_reg_clk: + clk_disable_unprepare(lpc18xx_wdt->reg_clk); + return ret; +} + +static void lpc18xx_wdt_shutdown(struct platform_device *pdev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev); + + lpc18xx_wdt_stop(&lpc18xx_wdt->wdt_dev); +} + +static int lpc18xx_wdt_remove(struct platform_device *pdev) +{ + struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev); + + unregister_restart_handler(&lpc18xx_wdt->restart_handler); + + dev_warn(&pdev->dev, "I quit now, hardware will probably reboot!\n"); + del_timer(&lpc18xx_wdt->timer); + + watchdog_unregister_device(&lpc18xx_wdt->wdt_dev); + clk_disable_unprepare(lpc18xx_wdt->wdt_clk); + clk_disable_unprepare(lpc18xx_wdt->reg_clk); + + return 0; +} + +static const struct of_device_id lpc18xx_wdt_match[] = { + { .compatible = "nxp,lpc1850-wwdt" }, + {} +}; +MODULE_DEVICE_TABLE(of, lpc18xx_wdt_match); + +static struct platform_driver lpc18xx_wdt_driver = { + .driver = { + .name = "lpc18xx-wdt", + .of_match_table = lpc18xx_wdt_match, + }, + .probe = lpc18xx_wdt_probe, + .remove = lpc18xx_wdt_remove, + .shutdown = lpc18xx_wdt_shutdown, +}; +module_platform_driver(lpc18xx_wdt_driver); + +MODULE_AUTHOR("Ariel D'Alessandro <ariel@vanguardiasur.com.ar>"); +MODULE_DESCRIPTION("NXP LPC18xx Watchdog Timer Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c index d193a5e79c38..098fa9c34d6d 100644 --- a/drivers/watchdog/mena21_wdt.c +++ b/drivers/watchdog/mena21_wdt.c @@ -197,6 +197,7 @@ static int a21_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(&a21_wdt, 30, &pdev->dev); watchdog_set_nowayout(&a21_wdt, nowayout); watchdog_set_drvdata(&a21_wdt, drv); + a21_wdt.parent = &pdev->dev; reset = a21_wdt_get_bootstatus(drv); if (reset == 2) @@ -252,6 +253,7 @@ static const struct of_device_id a21_wdt_ids[] = { { .compatible = "men,a021-wdt" }, { }, }; +MODULE_DEVICE_TABLE(of, a21_wdt_ids); static struct platform_driver a21_wdt_driver = { .probe = a21_wdt_probe, diff --git a/drivers/watchdog/menf21bmc_wdt.c b/drivers/watchdog/menf21bmc_wdt.c index 59f0913c7341..3aefddebb386 100644 --- a/drivers/watchdog/menf21bmc_wdt.c +++ b/drivers/watchdog/menf21bmc_wdt.c @@ -130,6 +130,7 @@ static int menf21bmc_wdt_probe(struct platform_device *pdev) drv_data->wdt.info = &menf21bmc_wdt_info; drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN; drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX; + drv_data->wdt.parent = &pdev->dev; drv_data->i2c_client = i2c_client; /* diff --git a/drivers/watchdog/moxart_wdt.c b/drivers/watchdog/moxart_wdt.c index 2789da2c0515..60b0605bd7e6 100644 --- a/drivers/watchdog/moxart_wdt.c +++ b/drivers/watchdog/moxart_wdt.c @@ -168,6 +168,7 @@ static const struct of_device_id moxart_watchdog_match[] = { { .compatible = "moxa,moxart-watchdog" }, { }, }; +MODULE_DEVICE_TABLE(of, moxart_watchdog_match); static struct platform_driver moxart_wdt_driver = { .probe = moxart_wdt_probe, diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index 689381a24887..5f2273aac37d 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -50,8 +50,12 @@ struct mpc8xxx_wdt_type { bool hw_enabled; }; -static struct mpc8xxx_wdt __iomem *wd_base; -static int mpc8xxx_wdt_init_late(void); +struct mpc8xxx_wdt_ddata { + struct mpc8xxx_wdt __iomem *base; + struct watchdog_device wdd; + struct timer_list timer; + spinlock_t lock; +}; static u16 timeout = 0xffff; module_param(timeout, ushort, 0); @@ -68,65 +72,59 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -/* - * We always prescale, but if someone really doesn't want to they can set this - * to 0 - */ -static int prescale = 1; - -static DEFINE_SPINLOCK(wdt_spinlock); - -static void mpc8xxx_wdt_keepalive(void) +static void mpc8xxx_wdt_keepalive(struct mpc8xxx_wdt_ddata *ddata) { /* Ping the WDT */ - spin_lock(&wdt_spinlock); - out_be16(&wd_base->swsrr, 0x556c); - out_be16(&wd_base->swsrr, 0xaa39); - spin_unlock(&wdt_spinlock); + spin_lock(&ddata->lock); + out_be16(&ddata->base->swsrr, 0x556c); + out_be16(&ddata->base->swsrr, 0xaa39); + spin_unlock(&ddata->lock); } -static struct watchdog_device mpc8xxx_wdt_dev; -static void mpc8xxx_wdt_timer_ping(unsigned long arg); -static DEFINE_TIMER(wdt_timer, mpc8xxx_wdt_timer_ping, 0, - (unsigned long)&mpc8xxx_wdt_dev); - static void mpc8xxx_wdt_timer_ping(unsigned long arg) { - struct watchdog_device *w = (struct watchdog_device *)arg; + struct mpc8xxx_wdt_ddata *ddata = (void *)arg; - mpc8xxx_wdt_keepalive(); + mpc8xxx_wdt_keepalive(ddata); /* We're pinging it twice faster than needed, just to be sure. */ - mod_timer(&wdt_timer, jiffies + HZ * w->timeout / 2); + mod_timer(&ddata->timer, jiffies + HZ * ddata->wdd.timeout / 2); } static int mpc8xxx_wdt_start(struct watchdog_device *w) { - u32 tmp = SWCRR_SWEN; + struct mpc8xxx_wdt_ddata *ddata = + container_of(w, struct mpc8xxx_wdt_ddata, wdd); + + u32 tmp = SWCRR_SWEN | SWCRR_SWPR; /* Good, fire up the show */ - if (prescale) - tmp |= SWCRR_SWPR; if (reset) tmp |= SWCRR_SWRI; tmp |= timeout << 16; - out_be32(&wd_base->swcrr, tmp); + out_be32(&ddata->base->swcrr, tmp); - del_timer_sync(&wdt_timer); + del_timer_sync(&ddata->timer); return 0; } static int mpc8xxx_wdt_ping(struct watchdog_device *w) { - mpc8xxx_wdt_keepalive(); + struct mpc8xxx_wdt_ddata *ddata = + container_of(w, struct mpc8xxx_wdt_ddata, wdd); + + mpc8xxx_wdt_keepalive(ddata); return 0; } static int mpc8xxx_wdt_stop(struct watchdog_device *w) { - mod_timer(&wdt_timer, jiffies); + struct mpc8xxx_wdt_ddata *ddata = + container_of(w, struct mpc8xxx_wdt_ddata, wdd); + + mod_timer(&ddata->timer, jiffies); return 0; } @@ -143,53 +141,57 @@ static struct watchdog_ops mpc8xxx_wdt_ops = { .stop = mpc8xxx_wdt_stop, }; -static struct watchdog_device mpc8xxx_wdt_dev = { - .info = &mpc8xxx_wdt_info, - .ops = &mpc8xxx_wdt_ops, -}; - -static const struct of_device_id mpc8xxx_wdt_match[]; static int mpc8xxx_wdt_probe(struct platform_device *ofdev) { int ret; - const struct of_device_id *match; - struct device_node *np = ofdev->dev.of_node; + struct resource *res; const struct mpc8xxx_wdt_type *wdt_type; + struct mpc8xxx_wdt_ddata *ddata; u32 freq = fsl_get_sys_freq(); bool enabled; unsigned int timeout_sec; - match = of_match_device(mpc8xxx_wdt_match, &ofdev->dev); - if (!match) + wdt_type = of_device_get_match_data(&ofdev->dev); + if (!wdt_type) return -EINVAL; - wdt_type = match->data; if (!freq || freq == -1) return -EINVAL; - wd_base = of_iomap(np, 0); - if (!wd_base) + ddata = devm_kzalloc(&ofdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) return -ENOMEM; - enabled = in_be32(&wd_base->swcrr) & SWCRR_SWEN; + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); + ddata->base = devm_ioremap_resource(&ofdev->dev, res); + if (IS_ERR(ddata->base)) + return PTR_ERR(ddata->base); + + enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN; if (!enabled && wdt_type->hw_enabled) { pr_info("could not be enabled in software\n"); - ret = -ENOSYS; - goto err_unmap; + return -ENODEV; } + spin_lock_init(&ddata->lock); + setup_timer(&ddata->timer, mpc8xxx_wdt_timer_ping, + (unsigned long)ddata); + + ddata->wdd.info = &mpc8xxx_wdt_info, + ddata->wdd.ops = &mpc8xxx_wdt_ops, + /* Calculate the timeout in seconds */ - if (prescale) - timeout_sec = (timeout * wdt_type->prescaler) / freq; - else - timeout_sec = timeout / freq; - - mpc8xxx_wdt_dev.timeout = timeout_sec; -#ifdef MODULE - ret = mpc8xxx_wdt_init_late(); - if (ret) - goto err_unmap; -#endif + timeout_sec = (timeout * wdt_type->prescaler) / freq; + + ddata->wdd.timeout = timeout_sec; + + watchdog_set_nowayout(&ddata->wdd, nowayout); + + ret = watchdog_register_device(&ddata->wdd); + if (ret) { + pr_err("cannot register watchdog device (err=%d)\n", ret); + return ret; + } pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d (%d seconds)\n", reset ? "reset" : "interrupt", timeout, timeout_sec); @@ -200,21 +202,20 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) * userspace handles it. */ if (enabled) - mod_timer(&wdt_timer, jiffies); + mod_timer(&ddata->timer, jiffies); + + platform_set_drvdata(ofdev, ddata); return 0; -err_unmap: - iounmap(wd_base); - wd_base = NULL; - return ret; } static int mpc8xxx_wdt_remove(struct platform_device *ofdev) { + struct mpc8xxx_wdt_ddata *ddata = platform_get_drvdata(ofdev); + pr_crit("Watchdog removed, expect the %s soon!\n", reset ? "reset" : "machine check exception"); - del_timer_sync(&wdt_timer); - watchdog_unregister_device(&mpc8xxx_wdt_dev); - iounmap(wd_base); + del_timer_sync(&ddata->timer); + watchdog_unregister_device(&ddata->wdd); return 0; } @@ -253,31 +254,6 @@ static struct platform_driver mpc8xxx_wdt_driver = { }, }; -/* - * We do wdt initialization in two steps: arch_initcall probes the wdt - * very early to start pinging the watchdog (misc devices are not yet - * available), and later module_init() just registers the misc device. - */ -static int mpc8xxx_wdt_init_late(void) -{ - int ret; - - if (!wd_base) - return -ENODEV; - - watchdog_set_nowayout(&mpc8xxx_wdt_dev, nowayout); - - ret = watchdog_register_device(&mpc8xxx_wdt_dev); - if (ret) { - pr_err("cannot register watchdog device (err=%d)\n", ret); - return ret; - } - return 0; -} -#ifndef MODULE -module_init(mpc8xxx_wdt_init_late); -#endif - static int __init mpc8xxx_wdt_init(void) { return platform_driver_register(&mpc8xxx_wdt_driver); diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index 938b987de551..6ad9df948711 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -210,6 +210,14 @@ static int mtk_wdt_probe(struct platform_device *pdev) return 0; } +static void mtk_wdt_shutdown(struct platform_device *pdev) +{ + struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); + + if (watchdog_active(&mtk_wdt->wdt_dev)) + mtk_wdt_stop(&mtk_wdt->wdt_dev); +} + static int mtk_wdt_remove(struct platform_device *pdev) { struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); @@ -221,17 +229,48 @@ static int mtk_wdt_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int mtk_wdt_suspend(struct device *dev) +{ + struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev); + + if (watchdog_active(&mtk_wdt->wdt_dev)) + mtk_wdt_stop(&mtk_wdt->wdt_dev); + + return 0; +} + +static int mtk_wdt_resume(struct device *dev) +{ + struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev); + + if (watchdog_active(&mtk_wdt->wdt_dev)) { + mtk_wdt_start(&mtk_wdt->wdt_dev); + mtk_wdt_ping(&mtk_wdt->wdt_dev); + } + + return 0; +} +#endif + static const struct of_device_id mtk_wdt_dt_ids[] = { { .compatible = "mediatek,mt6589-wdt" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); +static const struct dev_pm_ops mtk_wdt_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mtk_wdt_suspend, + mtk_wdt_resume) +}; + static struct platform_driver mtk_wdt_driver = { .probe = mtk_wdt_probe, .remove = mtk_wdt_remove, + .shutdown = mtk_wdt_shutdown, .driver = { .name = DRV_NAME, + .pm = &mtk_wdt_pm_ops, .of_match_table = mtk_wdt_dt_ids, }, }; diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c index c028454be66c..bd917bb757b8 100644 --- a/drivers/watchdog/nv_tco.c +++ b/drivers/watchdog/nv_tco.c @@ -294,6 +294,8 @@ static const struct pci_device_id tco_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS, + PCI_ANY_ID, PCI_ANY_ID, }, { 0, }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, tco_pci_tbl); diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index de911c7e477c..d96bee017fd3 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -253,6 +253,7 @@ static int omap_wdt_probe(struct platform_device *pdev) wdev->wdog.ops = &omap_wdt_ops; wdev->wdog.min_timeout = TIMER_MARGIN_MIN; wdev->wdog.max_timeout = TIMER_MARGIN_MAX; + wdev->wdog.parent = &pdev->dev; if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0) wdev->wdog.timeout = TIMER_MARGIN_DEFAULT; diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index ef0c628d5037..c6b8f4a43bde 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -567,6 +567,7 @@ static int orion_wdt_probe(struct platform_device *pdev) dev->wdt.timeout = wdt_max_duration; dev->wdt.max_timeout = wdt_max_duration; + dev->wdt.parent = &pdev->dev; watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev); platform_set_drvdata(pdev, &dev->wdt); diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c index b9c6049c3e78..4224b3ec83a5 100644 --- a/drivers/watchdog/pnx4008_wdt.c +++ b/drivers/watchdog/pnx4008_wdt.c @@ -167,6 +167,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev) pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ? WDIOF_CARDRESET : 0; + pnx4008_wdd.parent = &pdev->dev; watchdog_set_nowayout(&pnx4008_wdd, nowayout); pnx4008_wdt_stop(&pnx4008_wdd); /* disable for now */ diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index aa03ca8f2d9b..773dcfaee7b2 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -171,6 +171,7 @@ static int qcom_wdt_probe(struct platform_device *pdev) wdt->wdd.ops = &qcom_wdt_ops; wdt->wdd.min_timeout = 1; wdt->wdd.max_timeout = 0x10000000U / wdt->rate; + wdt->wdd.parent = &pdev->dev; /* * If 'timeout-sec' unspecified in devicetree, assume a 30 second diff --git a/drivers/watchdog/retu_wdt.c b/drivers/watchdog/retu_wdt.c index b7c68e275aeb..39cd51df2ffc 100644 --- a/drivers/watchdog/retu_wdt.c +++ b/drivers/watchdog/retu_wdt.c @@ -127,6 +127,7 @@ static int retu_wdt_probe(struct platform_device *pdev) retu_wdt->timeout = RETU_WDT_MAX_TIMER; retu_wdt->min_timeout = 0; retu_wdt->max_timeout = RETU_WDT_MAX_TIMER; + retu_wdt->parent = &pdev->dev; watchdog_set_drvdata(retu_wdt, wdev); watchdog_set_nowayout(retu_wdt, nowayout); diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c index a6f7e2e29beb..1967919ae743 100644 --- a/drivers/watchdog/rt2880_wdt.c +++ b/drivers/watchdog/rt2880_wdt.c @@ -161,6 +161,7 @@ static int rt288x_wdt_probe(struct platform_device *pdev) rt288x_wdt_dev.dev = &pdev->dev; rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause(); rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq); + rt288x_wdt_dev.parent = &pdev->dev; watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout, &pdev->dev); diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index e89ae027c91d..d781000c7825 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -607,6 +607,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(&wdt->wdt_device, nowayout); wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt); + wdt->wdt_device.parent = &pdev->dev; ret = watchdog_register_device(&wdt->wdt_device); if (ret) { diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c new file mode 100644 index 000000000000..a49634cdc1cc --- /dev/null +++ b/drivers/watchdog/sama5d4_wdt.c @@ -0,0 +1,280 @@ +/* + * Driver for Atmel SAMA5D4 Watchdog Timer + * + * Copyright (C) 2015 Atmel Corporation + * + * Licensed under GPLv2. + */ + +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/watchdog.h> + +#include "at91sam9_wdt.h" + +/* minimum and maximum watchdog timeout, in seconds */ +#define MIN_WDT_TIMEOUT 1 +#define MAX_WDT_TIMEOUT 16 +#define WDT_DEFAULT_TIMEOUT MAX_WDT_TIMEOUT + +#define WDT_SEC2TICKS(s) ((s) ? (((s) << 8) - 1) : 0) + +struct sama5d4_wdt { + struct watchdog_device wdd; + void __iomem *reg_base; + u32 config; +}; + +static int wdt_timeout = WDT_DEFAULT_TIMEOUT; +static bool nowayout = WATCHDOG_NOWAYOUT; + +module_param(wdt_timeout, int, 0); +MODULE_PARM_DESC(wdt_timeout, + "Watchdog timeout in seconds. (default = " + __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +#define wdt_read(wdt, field) \ + readl_relaxed((wdt)->reg_base + (field)) + +#define wdt_write(wtd, field, val) \ + writel_relaxed((val), (wdt)->reg_base + (field)) + +static int sama5d4_wdt_start(struct watchdog_device *wdd) +{ + struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); + u32 reg; + + reg = wdt_read(wdt, AT91_WDT_MR); + reg &= ~AT91_WDT_WDDIS; + wdt_write(wdt, AT91_WDT_MR, reg); + + return 0; +} + +static int sama5d4_wdt_stop(struct watchdog_device *wdd) +{ + struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); + u32 reg; + + reg = wdt_read(wdt, AT91_WDT_MR); + reg |= AT91_WDT_WDDIS; + wdt_write(wdt, AT91_WDT_MR, reg); + + return 0; +} + +static int sama5d4_wdt_ping(struct watchdog_device *wdd) +{ + struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); + + wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT); + + return 0; +} + +static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); + u32 value = WDT_SEC2TICKS(timeout); + u32 reg; + + reg = wdt_read(wdt, AT91_WDT_MR); + reg &= ~AT91_WDT_WDV; + reg &= ~AT91_WDT_WDD; + reg |= AT91_WDT_SET_WDV(value); + reg |= AT91_WDT_SET_WDD(value); + wdt_write(wdt, AT91_WDT_MR, reg); + + wdd->timeout = timeout; + + return 0; +} + +static const struct watchdog_info sama5d4_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, + .identity = "Atmel SAMA5D4 Watchdog", +}; + +static struct watchdog_ops sama5d4_wdt_ops = { + .owner = THIS_MODULE, + .start = sama5d4_wdt_start, + .stop = sama5d4_wdt_stop, + .ping = sama5d4_wdt_ping, + .set_timeout = sama5d4_wdt_set_timeout, +}; + +static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id) +{ + struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id); + + if (wdt_read(wdt, AT91_WDT_SR)) { + pr_crit("Atmel Watchdog Software Reset\n"); + emergency_restart(); + pr_crit("Reboot didn't succeed\n"); + } + + return IRQ_HANDLED; +} + +static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) +{ + const char *tmp; + + wdt->config = AT91_WDT_WDDIS; + + if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && + !strcmp(tmp, "software")) + wdt->config |= AT91_WDT_WDFIEN; + else + wdt->config |= AT91_WDT_WDRSTEN; + + if (of_property_read_bool(np, "atmel,idle-halt")) + wdt->config |= AT91_WDT_WDIDLEHLT; + + if (of_property_read_bool(np, "atmel,dbg-halt")) + wdt->config |= AT91_WDT_WDDBGHLT; + + return 0; +} + +static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) +{ + struct watchdog_device *wdd = &wdt->wdd; + u32 value = WDT_SEC2TICKS(wdd->timeout); + u32 reg; + + /* + * Because the fields WDV and WDD must not be modified when the WDDIS + * bit is set, so clear the WDDIS bit before writing the WDT_MR. + */ + reg = wdt_read(wdt, AT91_WDT_MR); + reg &= ~AT91_WDT_WDDIS; + wdt_write(wdt, AT91_WDT_MR, reg); + + reg = wdt->config; + reg |= AT91_WDT_SET_WDD(value); + reg |= AT91_WDT_SET_WDV(value); + + wdt_write(wdt, AT91_WDT_MR, reg); + + return 0; +} + +static int sama5d4_wdt_probe(struct platform_device *pdev) +{ + struct watchdog_device *wdd; + struct sama5d4_wdt *wdt; + struct resource *res; + void __iomem *regs; + u32 irq = 0; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdd = &wdt->wdd; + wdd->timeout = wdt_timeout; + wdd->info = &sama5d4_wdt_info; + wdd->ops = &sama5d4_wdt_ops; + wdd->min_timeout = MIN_WDT_TIMEOUT; + wdd->max_timeout = MAX_WDT_TIMEOUT; + + watchdog_set_drvdata(wdd, wdt); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + wdt->reg_base = regs; + + if (pdev->dev.of_node) { + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (!irq) + dev_warn(&pdev->dev, "failed to get IRQ from DT\n"); + + ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt); + if (ret) + return ret; + } + + if ((wdt->config & AT91_WDT_WDFIEN) && irq) { + ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler, + IRQF_SHARED | IRQF_IRQPOLL | + IRQF_NO_SUSPEND, pdev->name, pdev); + if (ret) { + dev_err(&pdev->dev, + "cannot register interrupt handler\n"); + return ret; + } + } + + ret = watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "unable to set timeout value\n"); + return ret; + } + + ret = sama5d4_wdt_init(wdt); + if (ret) + return ret; + + watchdog_set_nowayout(wdd, nowayout); + + ret = watchdog_register_device(wdd); + if (ret) { + dev_err(&pdev->dev, "failed to register watchdog device\n"); + return ret; + } + + platform_set_drvdata(pdev, wdt); + + dev_info(&pdev->dev, "initialized (timeout = %d sec, nowayout = %d)\n", + wdt_timeout, nowayout); + + return 0; +} + +static int sama5d4_wdt_remove(struct platform_device *pdev) +{ + struct sama5d4_wdt *wdt = platform_get_drvdata(pdev); + + sama5d4_wdt_stop(&wdt->wdd); + + watchdog_unregister_device(&wdt->wdd); + + return 0; +} + +static const struct of_device_id sama5d4_wdt_of_match[] = { + { .compatible = "atmel,sama5d4-wdt", }, + { } +}; +MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match); + +static struct platform_driver sama5d4_wdt_driver = { + .probe = sama5d4_wdt_probe, + .remove = sama5d4_wdt_remove, + .driver = { + .name = "sama5d4_wdt", + .of_match_table = sama5d4_wdt_of_match, + } +}; +module_platform_driver(sama5d4_wdt_driver); + +MODULE_AUTHOR("Atmel Corporation"); +MODULE_DESCRIPTION("Atmel SAMA5D4 Watchdog Timer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c index 567458b137a6..f90812170657 100644 --- a/drivers/watchdog/shwdt.c +++ b/drivers/watchdog/shwdt.c @@ -252,6 +252,7 @@ static int sh_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(&sh_wdt_dev, nowayout); watchdog_set_drvdata(&sh_wdt_dev, wdt); + sh_wdt_dev.parent = &pdev->dev; spin_lock_init(&wdt->lock); diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c index 42fa5c0c518a..d0578ab2e636 100644 --- a/drivers/watchdog/sirfsoc_wdt.c +++ b/drivers/watchdog/sirfsoc_wdt.c @@ -154,6 +154,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev); watchdog_set_nowayout(&sirfsoc_wdd, nowayout); + sirfsoc_wdd.parent = &pdev->dev; ret = watchdog_register_device(&sirfsoc_wdd); if (ret) diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index 4e7fec36f5c3..01d816251302 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c @@ -226,6 +226,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) wdt->adev = adev; wdt->wdd.info = &wdt_info; wdt->wdd.ops = &wdt_ops; + wdt->wdd.parent = &adev->dev; spin_lock_init(&wdt->lock); watchdog_set_nowayout(&wdt->wdd, nowayout); diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c index 6785afdc0fca..14e9badf2bfa 100644 --- a/drivers/watchdog/st_lpc_wdt.c +++ b/drivers/watchdog/st_lpc_wdt.c @@ -241,6 +241,7 @@ static int st_wdog_probe(struct platform_device *pdev) return -EINVAL; } st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate; + st_wdog_dev.parent = &pdev->dev; ret = clk_prepare_enable(clk); if (ret) { diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c index e7f0d5b60d3d..3ee6128a540e 100644 --- a/drivers/watchdog/stmp3xxx_rtc_wdt.c +++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c @@ -76,6 +76,7 @@ static int stmp3xxx_wdt_probe(struct platform_device *pdev) watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev); stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT); + stmp3xxx_wdd.parent = &pdev->dev; ret = watchdog_register_device(&stmp3xxx_wdd); if (ret < 0) { diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index a29afb37c48c..47bd8a14d01f 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -184,7 +184,7 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev) /* Set system reset function */ reg = readl(wdt_base + regs->wdt_cfg); reg &= ~(regs->wdt_reset_mask); - reg |= ~(regs->wdt_reset_val); + reg |= regs->wdt_reset_val; writel(reg, wdt_base + regs->wdt_cfg); /* Enable watchdog */ diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c index 30451ea46902..7f97cdd53f29 100644 --- a/drivers/watchdog/tegra_wdt.c +++ b/drivers/watchdog/tegra_wdt.c @@ -218,6 +218,7 @@ static int tegra_wdt_probe(struct platform_device *pdev) wdd->ops = &tegra_wdt_ops; wdd->min_timeout = MIN_WDT_TIMEOUT; wdd->max_timeout = MAX_WDT_TIMEOUT; + wdd->parent = &pdev->dev; watchdog_set_drvdata(wdd, wdt); diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c index 2c1db6fa9a27..9bf3cc0f3961 100644 --- a/drivers/watchdog/twl4030_wdt.c +++ b/drivers/watchdog/twl4030_wdt.c @@ -83,6 +83,7 @@ static int twl4030_wdt_probe(struct platform_device *pdev) wdt->timeout = 30; wdt->min_timeout = 1; wdt->max_timeout = 30; + wdt->parent = &pdev->dev; watchdog_set_nowayout(wdt, nowayout); platform_set_drvdata(pdev, wdt); diff --git a/drivers/watchdog/txx9wdt.c b/drivers/watchdog/txx9wdt.c index 7f615933d31a..c2da880292bc 100644 --- a/drivers/watchdog/txx9wdt.c +++ b/drivers/watchdog/txx9wdt.c @@ -131,6 +131,7 @@ static int __init txx9wdt_probe(struct platform_device *dev) txx9wdt.timeout = timeout; txx9wdt.min_timeout = 1; txx9wdt.max_timeout = WD_MAX_TIMEOUT; + txx9wdt.parent = &dev->dev; watchdog_set_nowayout(&txx9wdt, nowayout); ret = watchdog_register_device(&txx9wdt); diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c index 9de09ab00838..37c084353cce 100644 --- a/drivers/watchdog/ux500_wdt.c +++ b/drivers/watchdog/ux500_wdt.c @@ -96,6 +96,7 @@ static int ux500_wdt_probe(struct platform_device *pdev) ux500_wdt.max_timeout = WATCHDOG_MAX28; } + ux500_wdt.parent = &pdev->dev; watchdog_set_nowayout(&ux500_wdt, nowayout); /* disable auto off on sleep */ diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c index 56369c4f1961..5f9cbc37520d 100644 --- a/drivers/watchdog/via_wdt.c +++ b/drivers/watchdog/via_wdt.c @@ -206,6 +206,7 @@ static int wdt_probe(struct pci_dev *pdev, timeout = WDT_TIMEOUT; wdt_dev.timeout = timeout; + wdt_dev.parent = &pdev->dev; watchdog_set_nowayout(&wdt_dev, nowayout); if (readl(wdt_mem) & VIA_WDT_FIRED) wdt_dev.bootstatus |= WDIOF_CARDRESET; diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c index 2fa17e746ff6..8d1184aee932 100644 --- a/drivers/watchdog/wm831x_wdt.c +++ b/drivers/watchdog/wm831x_wdt.c @@ -215,6 +215,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev) wm831x_wdt->info = &wm831x_wdt_info; wm831x_wdt->ops = &wm831x_wdt_ops; + wm831x_wdt->parent = &pdev->dev; watchdog_set_nowayout(wm831x_wdt, nowayout); watchdog_set_drvdata(wm831x_wdt, driver_data); diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c index 34d272ada23d..4ab4b8347d45 100644 --- a/drivers/watchdog/wm8350_wdt.c +++ b/drivers/watchdog/wm8350_wdt.c @@ -151,6 +151,7 @@ static int wm8350_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(&wm8350_wdt, nowayout); watchdog_set_drvdata(&wm8350_wdt, wm8350); + wm8350_wdt.parent = &pdev->dev; /* Default to 4s timeout */ wm8350_wdt_set_timeout(&wm8350_wdt, 4); |
